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