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/objects/js-generator-inl.h" 16 #include "src/objects/module.h" 17 #include "src/parsing/parse-info.h" 18 #include "src/parsing/parsing.h" 19 #include "src/parsing/rewriter.h" 20 21 namespace v8 { 22 namespace internal { 23 24 ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector, 25 ScopeIterator::Option option) 26 : isolate_(isolate), 27 frame_inspector_(frame_inspector), 28 function_(frame_inspector_->GetFunction()), 29 script_(frame_inspector_->GetScript()) { 30 if (!frame_inspector->GetContext()->IsContext()) { 31 // Optimized frame, context or function cannot be materialized. Give up. 32 return; 33 } 34 context_ = Handle<Context>::cast(frame_inspector->GetContext()); 35 36 // We should not instantiate a ScopeIterator for wasm frames. 37 DCHECK_NE(Script::TYPE_WASM, frame_inspector->GetScript()->type()); 38 39 TryParseAndRetrieveScopes(option); 40 } 41 42 ScopeIterator::~ScopeIterator() { delete info_; } 43 44 Handle<Object> ScopeIterator::GetFunctionDebugName() const { 45 if (!function_.is_null()) return JSFunction::GetDebugName(function_); 46 47 if (!context_->IsNativeContext()) { 48 DisallowHeapAllocation no_gc; 49 ScopeInfo* closure_info = context_->closure_context()->scope_info(); 50 Handle<String> debug_name(closure_info->FunctionDebugName(), isolate_); 51 if (debug_name->length() > 0) return debug_name; 52 } 53 return isolate_->factory()->undefined_value(); 54 } 55 56 ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function) 57 : isolate_(isolate), 58 context_(function->context(), isolate), 59 script_(Script::cast(function->shared()->script()), isolate) { 60 if (!function->shared()->IsSubjectToDebugging()) { 61 context_ = Handle<Context>(); 62 return; 63 } 64 UnwrapEvaluationContext(); 65 } 66 67 ScopeIterator::ScopeIterator(Isolate* isolate, 68 Handle<JSGeneratorObject> generator) 69 : isolate_(isolate), 70 generator_(generator), 71 function_(generator->function(), isolate), 72 context_(generator->context(), isolate), 73 script_(Script::cast(function_->shared()->script()), isolate) { 74 if (!function_->shared()->IsSubjectToDebugging()) { 75 context_ = Handle<Context>(); 76 return; 77 } 78 TryParseAndRetrieveScopes(DEFAULT); 79 } 80 81 void ScopeIterator::Restart() { 82 DCHECK_NOT_NULL(frame_inspector_); 83 function_ = frame_inspector_->GetFunction(); 84 context_ = Handle<Context>::cast(frame_inspector_->GetContext()); 85 current_scope_ = start_scope_; 86 DCHECK_NOT_NULL(current_scope_); 87 UnwrapEvaluationContext(); 88 } 89 90 void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) { 91 // Catch the case when the debugger stops in an internal function. 92 Handle<SharedFunctionInfo> shared_info(function_->shared(), isolate_); 93 Handle<ScopeInfo> scope_info(shared_info->scope_info(), isolate_); 94 if (shared_info->script()->IsUndefined(isolate_)) { 95 current_scope_ = closure_scope_ = nullptr; 96 context_ = handle(function_->context(), isolate_); 97 function_ = Handle<JSFunction>(); 98 return; 99 } 100 101 DCHECK_NE(IGNORE_NESTED_SCOPES, option); 102 bool ignore_nested_scopes = false; 103 if (shared_info->HasBreakInfo() && frame_inspector_ != nullptr) { 104 // The source position at return is always the end of the function, 105 // which is not consistent with the current scope chain. Therefore all 106 // nested with, catch and block contexts are skipped, and we can only 107 // inspect the function scope. 108 // This can only happen if we set a break point inside right before the 109 // return, which requires a debug info to be available. 110 Handle<DebugInfo> debug_info(shared_info->GetDebugInfo(), isolate_); 111 112 // Find the break point where execution has stopped. 113 BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame()); 114 115 ignore_nested_scopes = location.IsReturn(); 116 } 117 118 // Reparse the code and analyze the scopes. 119 // Check whether we are in global, eval or function code. 120 if (scope_info->scope_type() == FUNCTION_SCOPE) { 121 // Inner function. 122 info_ = new ParseInfo(isolate_, shared_info); 123 } else { 124 // Global or eval code. 125 Handle<Script> script(Script::cast(shared_info->script()), isolate_); 126 info_ = new ParseInfo(isolate_, script); 127 if (scope_info->scope_type() == EVAL_SCOPE) { 128 info_->set_eval(); 129 if (!context_->IsNativeContext()) { 130 info_->set_outer_scope_info(handle(context_->scope_info(), isolate_)); 131 } 132 // Language mode may be inherited from the eval caller. 133 // Retrieve it from shared function info. 134 info_->set_language_mode(shared_info->language_mode()); 135 } else if (scope_info->scope_type() == MODULE_SCOPE) { 136 DCHECK(info_->is_module()); 137 } else { 138 DCHECK_EQ(SCRIPT_SCOPE, scope_info->scope_type()); 139 } 140 } 141 142 if (parsing::ParseAny(info_, shared_info, isolate_) && 143 Rewriter::Rewrite(info_)) { 144 info_->ast_value_factory()->Internalize(isolate_); 145 closure_scope_ = info_->literal()->scope(); 146 147 if (option == COLLECT_NON_LOCALS) { 148 DCHECK(non_locals_.is_null()); 149 non_locals_ = info_->literal()->scope()->CollectNonLocals( 150 isolate_, info_, StringSet::New(isolate_)); 151 } 152 153 CHECK(DeclarationScope::Analyze(info_)); 154 if (ignore_nested_scopes) { 155 current_scope_ = closure_scope_; 156 start_scope_ = current_scope_; 157 if (closure_scope_->NeedsContext()) { 158 context_ = handle(context_->closure_context(), isolate_); 159 } 160 } else { 161 RetrieveScopeChain(closure_scope_); 162 } 163 UnwrapEvaluationContext(); 164 } else { 165 // A failed reparse indicates that the preparser has diverged from the 166 // parser or that the preparse data given to the initial parse has been 167 // faulty. We fail in debug mode but in release mode we only provide the 168 // information we get from the context chain but nothing about 169 // completely stack allocated scopes or stack allocated locals. 170 // Or it could be due to stack overflow. 171 // Silently fail by presenting an empty context chain. 172 CHECK(isolate_->has_pending_exception()); 173 isolate_->clear_pending_exception(); 174 context_ = Handle<Context>(); 175 } 176 } 177 178 void ScopeIterator::UnwrapEvaluationContext() { 179 if (!context_->IsDebugEvaluateContext()) return; 180 Context* current = *context_; 181 do { 182 Object* wrapped = current->get(Context::WRAPPED_CONTEXT_INDEX); 183 if (wrapped->IsContext()) { 184 current = Context::cast(wrapped); 185 } else { 186 DCHECK_NOT_NULL(current->previous()); 187 current = current->previous(); 188 } 189 } while (current->IsDebugEvaluateContext()); 190 context_ = handle(current, isolate_); 191 } 192 193 Handle<JSObject> ScopeIterator::MaterializeScopeDetails() { 194 // Calculate the size of the result. 195 Handle<FixedArray> details = 196 isolate_->factory()->NewFixedArray(kScopeDetailsSize); 197 // Fill in scope details. 198 details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type())); 199 Handle<JSObject> scope_object = ScopeObject(Mode::ALL); 200 details->set(kScopeDetailsObjectIndex, *scope_object); 201 if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) { 202 return isolate_->factory()->NewJSArrayWithElements(details); 203 } else if (HasContext()) { 204 Handle<Object> closure_name = GetFunctionDebugName(); 205 details->set(kScopeDetailsNameIndex, *closure_name); 206 details->set(kScopeDetailsStartPositionIndex, 207 Smi::FromInt(start_position())); 208 details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position())); 209 if (InInnerScope()) { 210 details->set(kScopeDetailsFunctionIndex, *function_); 211 } 212 } 213 return isolate_->factory()->NewJSArrayWithElements(details); 214 } 215 216 bool ScopeIterator::HasPositionInfo() { 217 return InInnerScope() || !context_->IsNativeContext(); 218 } 219 220 int ScopeIterator::start_position() { 221 if (InInnerScope()) return current_scope_->start_position(); 222 if (context_->IsNativeContext()) return 0; 223 return context_->closure_context()->scope_info()->StartPosition(); 224 } 225 226 int ScopeIterator::end_position() { 227 if (InInnerScope()) return current_scope_->end_position(); 228 if (context_->IsNativeContext()) return 0; 229 return context_->closure_context()->scope_info()->EndPosition(); 230 } 231 232 bool ScopeIterator::DeclaresLocals(Mode mode) const { 233 ScopeType type = Type(); 234 235 if (type == ScopeTypeWith) return mode == Mode::ALL; 236 if (type == ScopeTypeGlobal) return mode == Mode::ALL; 237 238 bool declares_local = false; 239 auto visitor = [&](Handle<String> name, Handle<Object> value) { 240 declares_local = true; 241 return true; 242 }; 243 VisitScope(visitor, mode); 244 return declares_local; 245 } 246 247 bool ScopeIterator::HasContext() const { 248 return !InInnerScope() || current_scope_->NeedsContext(); 249 } 250 251 void ScopeIterator::Next() { 252 DCHECK(!Done()); 253 254 ScopeType scope_type = Type(); 255 256 if (scope_type == ScopeTypeGlobal) { 257 // The global scope is always the last in the chain. 258 DCHECK(context_->IsNativeContext()); 259 context_ = Handle<Context>(); 260 DCHECK(Done()); 261 return; 262 } 263 264 bool inner = InInnerScope(); 265 if (current_scope_ == closure_scope_) function_ = Handle<JSFunction>(); 266 267 if (scope_type == ScopeTypeScript) { 268 DCHECK_IMPLIES(InInnerScope(), current_scope_->is_script_scope()); 269 seen_script_scope_ = true; 270 if (context_->IsScriptContext()) { 271 context_ = handle(context_->previous(), isolate_); 272 } 273 } else if (!inner) { 274 DCHECK(!context_->IsNativeContext()); 275 context_ = handle(context_->previous(), isolate_); 276 } else { 277 DCHECK_NOT_NULL(current_scope_); 278 do { 279 if (current_scope_->NeedsContext()) { 280 DCHECK_NOT_NULL(context_->previous()); 281 context_ = handle(context_->previous(), isolate_); 282 } 283 DCHECK_IMPLIES(InInnerScope(), current_scope_->outer_scope() != nullptr); 284 current_scope_ = current_scope_->outer_scope(); 285 // Repeat to skip hidden scopes. 286 } while (current_scope_->is_hidden()); 287 } 288 289 UnwrapEvaluationContext(); 290 } 291 292 293 // Return the type of the current scope. 294 ScopeIterator::ScopeType ScopeIterator::Type() const { 295 DCHECK(!Done()); 296 if (InInnerScope()) { 297 switch (current_scope_->scope_type()) { 298 case FUNCTION_SCOPE: 299 DCHECK_IMPLIES(current_scope_->NeedsContext(), 300 context_->IsFunctionContext()); 301 return ScopeTypeLocal; 302 case MODULE_SCOPE: 303 DCHECK_IMPLIES(current_scope_->NeedsContext(), 304 context_->IsModuleContext()); 305 return ScopeTypeModule; 306 case SCRIPT_SCOPE: 307 DCHECK_IMPLIES( 308 current_scope_->NeedsContext(), 309 context_->IsScriptContext() || context_->IsNativeContext()); 310 return ScopeTypeScript; 311 case WITH_SCOPE: 312 DCHECK_IMPLIES( 313 current_scope_->NeedsContext(), 314 context_->IsWithContext() || context_->IsDebugEvaluateContext()); 315 return ScopeTypeWith; 316 case CATCH_SCOPE: 317 DCHECK(context_->IsCatchContext()); 318 return ScopeTypeCatch; 319 case BLOCK_SCOPE: 320 DCHECK_IMPLIES(current_scope_->NeedsContext(), 321 context_->IsBlockContext()); 322 return ScopeTypeBlock; 323 case EVAL_SCOPE: 324 DCHECK_IMPLIES(current_scope_->NeedsContext(), 325 context_->IsEvalContext()); 326 return ScopeTypeEval; 327 } 328 UNREACHABLE(); 329 } 330 if (context_->IsNativeContext()) { 331 DCHECK(context_->global_object()->IsJSGlobalObject()); 332 // If we are at the native context and have not yet seen script scope, 333 // fake it. 334 return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript; 335 } 336 if (context_->IsFunctionContext() || context_->IsEvalContext()) { 337 return ScopeTypeClosure; 338 } 339 if (context_->IsCatchContext()) { 340 return ScopeTypeCatch; 341 } 342 if (context_->IsBlockContext()) { 343 return ScopeTypeBlock; 344 } 345 if (context_->IsModuleContext()) { 346 return ScopeTypeModule; 347 } 348 if (context_->IsScriptContext()) { 349 return ScopeTypeScript; 350 } 351 DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext()); 352 return ScopeTypeWith; 353 } 354 355 Handle<JSObject> ScopeIterator::ScopeObject(Mode mode) { 356 DCHECK(!Done()); 357 358 ScopeType type = Type(); 359 if (type == ScopeTypeGlobal) { 360 DCHECK_EQ(Mode::ALL, mode); 361 return handle(context_->global_proxy(), isolate_); 362 } 363 if (type == ScopeTypeWith) { 364 DCHECK_EQ(Mode::ALL, mode); 365 return WithContextExtension(); 366 } 367 368 Handle<JSObject> scope = isolate_->factory()->NewJSObjectWithNullProto(); 369 auto visitor = [=](Handle<String> name, Handle<Object> value) { 370 JSObject::AddProperty(isolate_, scope, name, value, NONE); 371 return false; 372 }; 373 374 VisitScope(visitor, mode); 375 return scope; 376 } 377 378 void ScopeIterator::VisitScope(const Visitor& visitor, Mode mode) const { 379 switch (Type()) { 380 case ScopeTypeLocal: 381 case ScopeTypeClosure: 382 case ScopeTypeCatch: 383 case ScopeTypeBlock: 384 case ScopeTypeEval: 385 return VisitLocalScope(visitor, mode); 386 case ScopeTypeModule: 387 if (InInnerScope()) { 388 return VisitLocalScope(visitor, mode); 389 } 390 DCHECK_EQ(Mode::ALL, mode); 391 return VisitModuleScope(visitor); 392 case ScopeTypeScript: 393 DCHECK_EQ(Mode::ALL, mode); 394 return VisitScriptScope(visitor); 395 case ScopeTypeWith: 396 case ScopeTypeGlobal: 397 UNREACHABLE(); 398 } 399 } 400 401 bool ScopeIterator::SetVariableValue(Handle<String> name, 402 Handle<Object> value) { 403 DCHECK(!Done()); 404 name = isolate_->factory()->InternalizeString(name); 405 switch (Type()) { 406 case ScopeTypeGlobal: 407 case ScopeTypeWith: 408 break; 409 410 case ScopeTypeEval: 411 case ScopeTypeBlock: 412 case ScopeTypeCatch: 413 case ScopeTypeModule: 414 if (InInnerScope()) return SetLocalVariableValue(name, value); 415 if (Type() == ScopeTypeModule && SetModuleVariableValue(name, value)) { 416 return true; 417 } 418 return SetContextVariableValue(name, value); 419 420 case ScopeTypeLocal: 421 case ScopeTypeClosure: 422 if (InInnerScope()) { 423 DCHECK_EQ(ScopeTypeLocal, Type()); 424 if (SetLocalVariableValue(name, value)) return true; 425 // There may not be an associated context since we're InInnerScope(). 426 if (!current_scope_->NeedsContext()) return false; 427 } else { 428 DCHECK_EQ(ScopeTypeClosure, Type()); 429 if (SetContextVariableValue(name, value)) return true; 430 } 431 // The above functions only set variables statically declared in the 432 // function. There may be eval-introduced variables. Check them in 433 // SetContextExtensionValue. 434 return SetContextExtensionValue(name, value); 435 436 case ScopeTypeScript: 437 return SetScriptVariableValue(name, value); 438 } 439 return false; 440 } 441 442 Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; } 443 444 #ifdef DEBUG 445 // Debug print of the content of the current scope. 446 void ScopeIterator::DebugPrint() { 447 StdoutStream os; 448 DCHECK(!Done()); 449 switch (Type()) { 450 case ScopeIterator::ScopeTypeGlobal: 451 os << "Global:\n"; 452 context_->Print(os); 453 break; 454 455 case ScopeIterator::ScopeTypeLocal: { 456 os << "Local:\n"; 457 if (current_scope_->NeedsContext()) { 458 context_->Print(os); 459 if (context_->has_extension()) { 460 Handle<HeapObject> extension(context_->extension(), isolate_); 461 DCHECK(extension->IsJSContextExtensionObject()); 462 extension->Print(os); 463 } 464 } 465 break; 466 } 467 468 case ScopeIterator::ScopeTypeWith: 469 os << "With:\n"; 470 context_->extension()->Print(os); 471 break; 472 473 case ScopeIterator::ScopeTypeCatch: 474 os << "Catch:\n"; 475 context_->extension()->Print(os); 476 context_->get(Context::THROWN_OBJECT_INDEX)->Print(os); 477 break; 478 479 case ScopeIterator::ScopeTypeClosure: 480 os << "Closure:\n"; 481 context_->Print(os); 482 if (context_->has_extension()) { 483 Handle<HeapObject> extension(context_->extension(), isolate_); 484 DCHECK(extension->IsJSContextExtensionObject()); 485 extension->Print(os); 486 } 487 break; 488 489 case ScopeIterator::ScopeTypeScript: 490 os << "Script:\n"; 491 context_->global_object() 492 ->native_context() 493 ->script_context_table() 494 ->Print(os); 495 break; 496 497 default: 498 UNREACHABLE(); 499 } 500 PrintF("\n"); 501 } 502 #endif 503 504 int ScopeIterator::GetSourcePosition() { 505 if (frame_inspector_) { 506 return frame_inspector_->GetSourcePosition(); 507 } else { 508 DCHECK(!generator_.is_null()); 509 return generator_->source_position(); 510 } 511 } 512 513 void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) { 514 DCHECK_NOT_NULL(scope); 515 516 const int position = GetSourcePosition(); 517 518 Scope* parent = nullptr; 519 Scope* current = scope; 520 while (parent != current) { 521 parent = current; 522 for (Scope* inner_scope = current->inner_scope(); inner_scope != nullptr; 523 inner_scope = inner_scope->sibling()) { 524 int beg_pos = inner_scope->start_position(); 525 int end_pos = inner_scope->end_position(); 526 DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden()); 527 if (beg_pos <= position && position < end_pos) { 528 // Don't walk into inner functions. 529 if (!inner_scope->is_function_scope()) { 530 current = inner_scope; 531 } 532 break; 533 } 534 } 535 } 536 537 start_scope_ = current; 538 current_scope_ = current; 539 } 540 541 void ScopeIterator::VisitScriptScope(const Visitor& visitor) const { 542 Handle<JSGlobalObject> global(context_->global_object(), isolate_); 543 Handle<ScriptContextTable> script_contexts( 544 global->native_context()->script_context_table(), isolate_); 545 546 // Skip the first script since that just declares 'this'. 547 for (int context_index = 1; context_index < script_contexts->used(); 548 context_index++) { 549 Handle<Context> context = ScriptContextTable::GetContext( 550 isolate_, script_contexts, context_index); 551 Handle<ScopeInfo> scope_info(context->scope_info(), isolate_); 552 if (VisitContextLocals(visitor, scope_info, context)) return; 553 } 554 } 555 556 void ScopeIterator::VisitModuleScope(const Visitor& visitor) const { 557 DCHECK(context_->IsModuleContext()); 558 559 Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_); 560 if (VisitContextLocals(visitor, scope_info, context_)) return; 561 562 int count_index = scope_info->ModuleVariableCountIndex(); 563 int module_variable_count = Smi::cast(scope_info->get(count_index))->value(); 564 565 Handle<Module> module(context_->module(), isolate_); 566 567 for (int i = 0; i < module_variable_count; ++i) { 568 int index; 569 Handle<String> name; 570 { 571 String* raw_name; 572 scope_info->ModuleVariable(i, &raw_name, &index); 573 CHECK(!ScopeInfo::VariableIsSynthetic(raw_name)); 574 name = handle(raw_name, isolate_); 575 } 576 Handle<Object> value = Module::LoadVariable(isolate_, module, index); 577 578 // Reflect variables under TDZ as undeclared in scope object. 579 if (value->IsTheHole(isolate_)) continue; 580 if (visitor(name, value)) return; 581 } 582 } 583 584 bool ScopeIterator::VisitContextLocals(const Visitor& visitor, 585 Handle<ScopeInfo> scope_info, 586 Handle<Context> context) const { 587 // Fill all context locals to the context extension. 588 for (int i = 0; i < scope_info->ContextLocalCount(); ++i) { 589 Handle<String> name(scope_info->ContextLocalName(i), isolate_); 590 if (ScopeInfo::VariableIsSynthetic(*name)) continue; 591 int context_index = Context::MIN_CONTEXT_SLOTS + i; 592 Handle<Object> value(context->get(context_index), isolate_); 593 // Reflect variables under TDZ as undefined in scope object. 594 if (value->IsTheHole(isolate_)) continue; 595 if (visitor(name, value)) return true; 596 } 597 return false; 598 } 599 600 bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode) const { 601 for (Variable* var : *current_scope_->locals()) { 602 if (!var->is_this() && ScopeInfo::VariableIsSynthetic(*var->name())) { 603 continue; 604 } 605 606 int index = var->index(); 607 Handle<Object> value; 608 switch (var->location()) { 609 case VariableLocation::LOOKUP: 610 UNREACHABLE(); 611 break; 612 613 case VariableLocation::UNALLOCATED: 614 if (!var->is_this()) continue; 615 // No idea why we only add it sometimes. 616 if (mode == Mode::ALL) continue; 617 // No idea why this diverges... 618 value = frame_inspector_->GetReceiver(); 619 break; 620 621 case VariableLocation::PARAMETER: { 622 if (frame_inspector_ == nullptr) { 623 // Get the variable from the suspended generator. 624 DCHECK(!generator_.is_null()); 625 if (var->is_this()) { 626 value = handle(generator_->receiver(), isolate_); 627 } else { 628 FixedArray* parameters_and_registers = 629 generator_->parameters_and_registers(); 630 DCHECK_LT(index, parameters_and_registers->length()); 631 value = handle(parameters_and_registers->get(index), isolate_); 632 } 633 } else { 634 value = var->is_this() ? frame_inspector_->GetReceiver() 635 : frame_inspector_->GetParameter(index); 636 637 if (value->IsOptimizedOut(isolate_)) { 638 value = isolate_->factory()->undefined_value(); 639 } else if (var->is_this() && value->IsTheHole(isolate_)) { 640 value = isolate_->factory()->undefined_value(); 641 } 642 } 643 break; 644 } 645 646 case VariableLocation::LOCAL: 647 if (frame_inspector_ == nullptr) { 648 // Get the variable from the suspended generator. 649 DCHECK(!generator_.is_null()); 650 FixedArray* parameters_and_registers = 651 generator_->parameters_and_registers(); 652 int parameter_count = 653 function_->shared()->scope_info()->ParameterCount(); 654 index += parameter_count; 655 DCHECK_LT(index, parameters_and_registers->length()); 656 value = handle(parameters_and_registers->get(index), isolate_); 657 if (value->IsTheHole(isolate_)) { 658 value = isolate_->factory()->undefined_value(); 659 } 660 } else { 661 value = frame_inspector_->GetExpression(index); 662 if (value->IsOptimizedOut(isolate_)) { 663 // We'll rematerialize this later. 664 if (current_scope_->is_declaration_scope() && 665 current_scope_->AsDeclarationScope()->arguments() == var) { 666 continue; 667 } 668 value = isolate_->factory()->undefined_value(); 669 } else if (value->IsTheHole(isolate_)) { 670 // Reflect variables under TDZ as undeclared in scope object. 671 continue; 672 } 673 } 674 break; 675 676 case VariableLocation::CONTEXT: 677 if (mode == Mode::STACK) continue; 678 // TODO(verwaest): Why don't we want to show it if it's there?... 679 if (var->is_this()) continue; 680 DCHECK(var->IsContextSlot()); 681 value = handle(context_->get(index), isolate_); 682 // Reflect variables under TDZ as undeclared in scope object. 683 if (value->IsTheHole(isolate_)) continue; 684 break; 685 686 case VariableLocation::MODULE: { 687 if (mode == Mode::STACK) continue; 688 // if (var->IsExport()) continue; 689 Handle<Module> module(context_->module(), isolate_); 690 value = Module::LoadVariable(isolate_, module, var->index()); 691 // Reflect variables under TDZ as undeclared in scope object. 692 if (value->IsTheHole(isolate_)) continue; 693 break; 694 } 695 } 696 697 if (visitor(var->name(), value)) return true; 698 } 699 return false; 700 } 701 702 // Retrieve the with-context extension object. If the extension object is 703 // a proxy, return an empty object. 704 Handle<JSObject> ScopeIterator::WithContextExtension() { 705 DCHECK(context_->IsWithContext()); 706 if (context_->extension_receiver()->IsJSProxy()) { 707 return isolate_->factory()->NewJSObjectWithNullProto(); 708 } 709 return handle(JSObject::cast(context_->extension_receiver()), isolate_); 710 } 711 712 // Create a plain JSObject which materializes the block scope for the specified 713 // block context. 714 void ScopeIterator::VisitLocalScope(const Visitor& visitor, Mode mode) const { 715 if (InInnerScope()) { 716 if (VisitLocals(visitor, mode)) return; 717 if (mode == Mode::STACK && Type() == ScopeTypeLocal) { 718 // Hide |this| in arrow functions that may be embedded in other functions 719 // but don't force |this| to be context-allocated. Otherwise we'd find the 720 // wrong |this| value. 721 if (!closure_scope_->has_this_declaration() && 722 !non_locals_->Has(isolate_, isolate_->factory()->this_string())) { 723 if (visitor(isolate_->factory()->this_string(), 724 isolate_->factory()->undefined_value())) 725 return; 726 } 727 // Add |arguments| to the function scope even if it wasn't used. 728 // Currently we don't yet support materializing the arguments object of 729 // suspended generators. We'd need to read the arguments out from the 730 // suspended generator rather than from an activation as 731 // FunctionGetArguments does. 732 if (frame_inspector_ != nullptr && !closure_scope_->is_arrow_scope() && 733 (closure_scope_->arguments() == nullptr || 734 frame_inspector_->GetExpression(closure_scope_->arguments()->index()) 735 ->IsOptimizedOut(isolate_))) { 736 JavaScriptFrame* frame = GetFrame(); 737 Handle<JSObject> arguments = Accessors::FunctionGetArguments( 738 frame, frame_inspector_->inlined_frame_index()); 739 if (visitor(isolate_->factory()->arguments_string(), arguments)) return; 740 } 741 } 742 } else { 743 DCHECK_EQ(Mode::ALL, mode); 744 Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_); 745 if (VisitContextLocals(visitor, scope_info, context_)) return; 746 } 747 748 if (mode == Mode::ALL && HasContext()) { 749 DCHECK(!context_->IsScriptContext()); 750 DCHECK(!context_->IsNativeContext()); 751 DCHECK(!context_->IsWithContext()); 752 if (!context_->scope_info()->CallsSloppyEval()) return; 753 if (context_->extension_object() == nullptr) return; 754 Handle<JSObject> extension(context_->extension_object(), isolate_); 755 Handle<FixedArray> keys = 756 KeyAccumulator::GetKeys(extension, KeyCollectionMode::kOwnOnly, 757 ENUMERABLE_STRINGS) 758 .ToHandleChecked(); 759 760 for (int i = 0; i < keys->length(); i++) { 761 // Names of variables introduced by eval are strings. 762 DCHECK(keys->get(i)->IsString()); 763 Handle<String> key(String::cast(keys->get(i)), isolate_); 764 Handle<Object> value = JSReceiver::GetDataProperty(extension, key); 765 if (visitor(key, value)) return; 766 } 767 } 768 } 769 770 bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name, 771 Handle<Object> new_value) { 772 // TODO(verwaest): Walk parameters backwards, not forwards. 773 // TODO(verwaest): Use VariableMap rather than locals() list for lookup. 774 for (Variable* var : *current_scope_->locals()) { 775 if (String::Equals(isolate_, var->name(), variable_name)) { 776 int index = var->index(); 777 switch (var->location()) { 778 case VariableLocation::LOOKUP: 779 case VariableLocation::UNALLOCATED: 780 // Drop assignments to unallocated locals. 781 DCHECK(var->is_this() || 782 *variable_name == ReadOnlyRoots(isolate_).arguments_string()); 783 return false; 784 785 case VariableLocation::PARAMETER: { 786 if (var->is_this()) return false; 787 if (frame_inspector_ == nullptr) { 788 // Set the variable in the suspended generator. 789 DCHECK(!generator_.is_null()); 790 Handle<FixedArray> parameters_and_registers( 791 generator_->parameters_and_registers(), isolate_); 792 DCHECK_LT(index, parameters_and_registers->length()); 793 parameters_and_registers->set(index, *new_value); 794 } else { 795 JavaScriptFrame* frame = GetFrame(); 796 if (frame->is_optimized()) return false; 797 798 frame->SetParameterValue(index, *new_value); 799 } 800 return true; 801 } 802 803 case VariableLocation::LOCAL: 804 if (frame_inspector_ == nullptr) { 805 // Set the variable in the suspended generator. 806 DCHECK(!generator_.is_null()); 807 int parameter_count = 808 function_->shared()->scope_info()->ParameterCount(); 809 index += parameter_count; 810 Handle<FixedArray> parameters_and_registers( 811 generator_->parameters_and_registers(), isolate_); 812 DCHECK_LT(index, parameters_and_registers->length()); 813 parameters_and_registers->set(index, *new_value); 814 } else { 815 // Set the variable on the stack. 816 JavaScriptFrame* frame = GetFrame(); 817 if (frame->is_optimized()) return false; 818 819 frame->SetExpression(index, *new_value); 820 } 821 return true; 822 823 case VariableLocation::CONTEXT: 824 DCHECK(var->IsContextSlot()); 825 context_->set(index, *new_value); 826 return true; 827 828 case VariableLocation::MODULE: 829 if (!var->IsExport()) return false; 830 Handle<Module> module(context_->module(), isolate_); 831 Module::StoreVariable(module, var->index(), new_value); 832 return true; 833 } 834 UNREACHABLE(); 835 } 836 } 837 838 return false; 839 } 840 841 bool ScopeIterator::SetContextExtensionValue(Handle<String> variable_name, 842 Handle<Object> new_value) { 843 if (!context_->has_extension()) return false; 844 845 DCHECK(context_->extension_object()->IsJSContextExtensionObject()); 846 Handle<JSObject> ext(context_->extension_object(), isolate_); 847 LookupIterator it(isolate_, ext, variable_name, LookupIterator::OWN); 848 Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name); 849 DCHECK(maybe.IsJust()); 850 if (!maybe.FromJust()) return false; 851 852 CHECK(Object::SetDataProperty(&it, new_value).ToChecked()); 853 return true; 854 } 855 856 bool ScopeIterator::SetContextVariableValue(Handle<String> variable_name, 857 Handle<Object> new_value) { 858 Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_); 859 860 VariableMode mode; 861 InitializationFlag flag; 862 MaybeAssignedFlag maybe_assigned_flag; 863 int slot_index = ScopeInfo::ContextSlotIndex(scope_info, variable_name, &mode, 864 &flag, &maybe_assigned_flag); 865 if (slot_index < 0) return false; 866 867 context_->set(slot_index, *new_value); 868 return true; 869 } 870 871 bool ScopeIterator::SetModuleVariableValue(Handle<String> variable_name, 872 Handle<Object> new_value) { 873 int cell_index; 874 VariableMode mode; 875 InitializationFlag init_flag; 876 MaybeAssignedFlag maybe_assigned_flag; 877 cell_index = context_->scope_info()->ModuleIndex( 878 variable_name, &mode, &init_flag, &maybe_assigned_flag); 879 880 // Setting imports is currently not supported. 881 if (ModuleDescriptor::GetCellIndexKind(cell_index) != 882 ModuleDescriptor::kExport) { 883 return false; 884 } 885 886 Handle<Module> module(context_->module(), isolate_); 887 Module::StoreVariable(module, cell_index, new_value); 888 return true; 889 } 890 891 bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name, 892 Handle<Object> new_value) { 893 Handle<ScriptContextTable> script_contexts( 894 context_->global_object()->native_context()->script_context_table(), 895 isolate_); 896 ScriptContextTable::LookupResult lookup_result; 897 if (ScriptContextTable::Lookup(isolate_, script_contexts, variable_name, 898 &lookup_result)) { 899 Handle<Context> script_context = ScriptContextTable::GetContext( 900 isolate_, script_contexts, lookup_result.context_index); 901 script_context->set(lookup_result.slot_index, *new_value); 902 return true; 903 } 904 905 return false; 906 } 907 908 } // namespace internal 909 } // namespace v8 910