debug-scopes.cc 31.4 KB
Newer Older
1 2 3 4 5 6
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/debug/debug-scopes.h"

7 8
#include <memory>

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"
verwaest's avatar
verwaest committed
19
#include "src/parsing/rewriter.h"
20 21 22 23 24

namespace v8 {
namespace internal {

ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
25
                             ScopeIterator::Option option)
26 27
    : isolate_(isolate),
      frame_inspector_(frame_inspector),
28
      function_(frame_inspector_->GetFunction()),
29
      script_(frame_inspector_->GetScript()) {
30
  if (!frame_inspector->GetContext()->IsContext()) {
31 32 33
    // Optimized frame, context or function cannot be materialized. Give up.
    return;
  }
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 40 41
  TryParseAndRetrieveScopes(option);
}

42 43
ScopeIterator::~ScopeIterator() { delete info_; }

44
Handle<Object> ScopeIterator::GetFunctionDebugName() const {
45 46
  if (!function_.is_null()) return JSFunction::GetDebugName(function_);

47 48
  if (!context_->IsNativeContext()) {
    DisallowHeapAllocation no_gc;
49
    ScopeInfo* closure_info = context_->closure_context()->scope_info();
50
    Handle<String> debug_name(closure_info->FunctionDebugName(), isolate_);
51 52 53 54 55 56 57
    if (debug_name->length() > 0) return debug_name;
  }
  return isolate_->factory()->undefined_value();
}

ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
    : isolate_(isolate),
58 59
      context_(function->context(), isolate),
      script_(Script::cast(function->shared()->script()), isolate) {
60 61 62 63
  if (!function->shared()->IsSubjectToDebugging()) {
    context_ = Handle<Context>();
    return;
  }
64 65 66 67 68 69 70
  UnwrapEvaluationContext();
}

ScopeIterator::ScopeIterator(Isolate* isolate,
                             Handle<JSGeneratorObject> generator)
    : isolate_(isolate),
      generator_(generator),
71 72 73
      function_(generator->function(), isolate),
      context_(generator->context(), isolate),
      script_(Script::cast(function_->shared()->script()), isolate) {
74 75 76 77 78 79
  if (!function_->shared()->IsSubjectToDebugging()) {
    context_ = Handle<Context>();
    return;
  }
  TryParseAndRetrieveScopes(DEFAULT);
}
80

81 82 83 84 85 86 87 88 89
void ScopeIterator::Restart() {
  DCHECK_NOT_NULL(frame_inspector_);
  function_ = frame_inspector_->GetFunction();
  context_ = Handle<Context>::cast(frame_inspector_->GetContext());
  current_scope_ = start_scope_;
  DCHECK_NOT_NULL(current_scope_);
  UnwrapEvaluationContext();
}

90
void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
91
  // Catch the case when the debugger stops in an internal function.
92 93
  Handle<SharedFunctionInfo> shared_info(function_->shared(), isolate_);
  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 99 100
    return;
  }

101 102 103
  DCHECK_NE(IGNORE_NESTED_SCOPES, option);
  bool ignore_nested_scopes = false;
  if (shared_info->HasBreakInfo() && frame_inspector_ != nullptr) {
104 105 106 107 108 109
    // The source position at return is always the end of the function,
    // which is not consistent with the current scope chain. Therefore all
    // nested with, catch and block contexts are skipped, and we can only
    // inspect the function scope.
    // This can only happen if we set a break point inside right before the
    // 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 116 117

    ignore_nested_scopes = location.IsReturn();
  }

118 119
  // Reparse the code and analyze the scopes.
  // Check whether we are in global, eval or function code.
120 121 122 123
  if (scope_info->scope_type() == FUNCTION_SCOPE) {
    // Inner function.
    info_ = new ParseInfo(isolate_, shared_info);
  } 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 129 130
      info_->set_eval();
      if (!context_->IsNativeContext()) {
        info_->set_outer_scope_info(handle(context_->scope_info(), isolate_));
131
      }
132 133
      // Language mode may be inherited from the eval caller.
      // 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 143 144 145 146 147 148 149
  }

  if (parsing::ParseAny(info_, shared_info, isolate_) &&
      Rewriter::Rewrite(info_)) {
    info_->ast_value_factory()->Internalize(isolate_);
    closure_scope_ = info_->literal()->scope();

    if (option == COLLECT_NON_LOCALS) {
      DCHECK(non_locals_.is_null());
      non_locals_ = info_->literal()->scope()->CollectNonLocals(
150
          isolate_, info_, StringSet::New(isolate_));
verwaest's avatar
verwaest committed
151
    }
152 153 154 155 156 157 158

    CHECK(DeclarationScope::Analyze(info_));
    if (ignore_nested_scopes) {
      current_scope_ = closure_scope_;
      start_scope_ = current_scope_;
      if (closure_scope_->NeedsContext()) {
        context_ = handle(context_->closure_context(), isolate_);
159
      }
160 161
    } else {
      RetrieveScopeChain(closure_scope_);
verwaest's avatar
verwaest committed
162
    }
163
    UnwrapEvaluationContext();
164
  } else {
verwaest's avatar
verwaest committed
165 166 167 168 169 170
    // A failed reparse indicates that the preparser has diverged from the
    // parser or that the preparse data given to the initial parse has been
    // faulty. We fail in debug mode but in release mode we only provide the
    // information we get from the context chain but nothing about
    // completely stack allocated scopes or stack allocated locals.
    // Or it could be due to stack overflow.
171 172 173 174
    // Silently fail by presenting an empty context chain.
    CHECK(isolate_->has_pending_exception());
    isolate_->clear_pending_exception();
    context_ = Handle<Context>();
verwaest's avatar
verwaest committed
175
  }
176 177
}

178
void ScopeIterator::UnwrapEvaluationContext() {
179 180 181 182
  if (!context_->IsDebugEvaluateContext()) return;
  Context* current = *context_;
  do {
    Object* wrapped = current->get(Context::WRAPPED_CONTEXT_INDEX);
183
    if (wrapped->IsContext()) {
184
      current = Context::cast(wrapped);
185
    } else {
186 187
      DCHECK_NOT_NULL(current->previous());
      current = current->previous();
188
    }
189 190
  } while (current->IsDebugEvaluateContext());
  context_ = handle(current, isolate_);
191 192
}

193
Handle<JSObject> ScopeIterator::MaterializeScopeDetails() {
194 195 196 197 198
  // Calculate the size of the result.
  Handle<FixedArray> details =
      isolate_->factory()->NewFixedArray(kScopeDetailsSize);
  // Fill in scope details.
  details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
199
  Handle<JSObject> scope_object = ScopeObject(Mode::ALL);
200
  details->set(kScopeDetailsObjectIndex, *scope_object);
201 202
  if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
    return isolate_->factory()->NewJSArrayWithElements(details);
203 204 205
  } else if (HasContext()) {
    Handle<Object> closure_name = GetFunctionDebugName();
    details->set(kScopeDetailsNameIndex, *closure_name);
206 207 208
    details->set(kScopeDetailsStartPositionIndex,
                 Smi::FromInt(start_position()));
    details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position()));
209
    if (InInnerScope()) {
210 211
      details->set(kScopeDetailsFunctionIndex, *function_);
    }
212
  }
213 214 215
  return isolate_->factory()->NewJSArrayWithElements(details);
}

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 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
bool ScopeIterator::DeclaresLocals(Mode mode) const {
  ScopeType type = Type();

  if (type == ScopeTypeWith) return mode == Mode::ALL;
  if (type == ScopeTypeGlobal) return mode == Mode::ALL;

  bool declares_local = false;
  auto visitor = [&](Handle<String> name, Handle<Object> value) {
    declares_local = true;
    return true;
  };
  VisitScope(visitor, mode);
  return declares_local;
}

bool ScopeIterator::HasContext() const {
  return !InInnerScope() || current_scope_->NeedsContext();
}

251
void ScopeIterator::Next() {
252
  DCHECK(!Done());
253

254
  ScopeType scope_type = Type();
255

256 257 258 259
  if (scope_type == ScopeTypeGlobal) {
    // The global scope is always the last in the chain.
    DCHECK(context_->IsNativeContext());
    context_ = Handle<Context>();
260 261 262 263 264 265 266 267 268
    DCHECK(Done());
    return;
  }

  bool inner = InInnerScope();
  if (current_scope_ == closure_scope_) function_ = Handle<JSFunction>();

  if (scope_type == ScopeTypeScript) {
    DCHECK_IMPLIES(InInnerScope(), current_scope_->is_script_scope());
269 270
    seen_script_scope_ = true;
    if (context_->IsScriptContext()) {
271
      context_ = handle(context_->previous(), isolate_);
272
    }
273 274 275
  } else if (!inner) {
    DCHECK(!context_->IsNativeContext());
    context_ = handle(context_->previous(), isolate_);
276
  } else {
277
    DCHECK_NOT_NULL(current_scope_);
278
    do {
279 280 281
      if (current_scope_->NeedsContext()) {
        DCHECK_NOT_NULL(context_->previous());
        context_ = handle(context_->previous(), isolate_);
282
      }
283 284
      DCHECK_IMPLIES(InInnerScope(), current_scope_->outer_scope() != nullptr);
      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 297
  if (InInnerScope()) {
    switch (current_scope_->scope_type()) {
298
      case FUNCTION_SCOPE:
299 300
        DCHECK_IMPLIES(current_scope_->NeedsContext(),
                       context_->IsFunctionContext());
301 302
        return ScopeTypeLocal;
      case MODULE_SCOPE:
303 304
        DCHECK_IMPLIES(current_scope_->NeedsContext(),
                       context_->IsModuleContext());
305 306
        return ScopeTypeModule;
      case SCRIPT_SCOPE:
307 308 309
        DCHECK_IMPLIES(
            current_scope_->NeedsContext(),
            context_->IsScriptContext() || context_->IsNativeContext());
310 311
        return ScopeTypeScript;
      case WITH_SCOPE:
312 313 314
        DCHECK_IMPLIES(
            current_scope_->NeedsContext(),
            context_->IsWithContext() || context_->IsDebugEvaluateContext());
315 316 317 318 319
        return ScopeTypeWith;
      case CATCH_SCOPE:
        DCHECK(context_->IsCatchContext());
        return ScopeTypeCatch;
      case BLOCK_SCOPE:
320 321
        DCHECK_IMPLIES(current_scope_->NeedsContext(),
                       context_->IsBlockContext());
322 323
        return ScopeTypeBlock;
      case EVAL_SCOPE:
324 325
        DCHECK_IMPLIES(current_scope_->NeedsContext(),
                       context_->IsEvalContext());
326
        return ScopeTypeEval;
327
    }
328
    UNREACHABLE();
329 330
  }
  if (context_->IsNativeContext()) {
331
    DCHECK(context_->global_object()->IsJSGlobalObject());
332 333 334 335
    // If we are at the native context and have not yet seen script scope,
    // fake it.
    return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
  }
336
  if (context_->IsFunctionContext() || context_->IsEvalContext()) {
337 338 339 340 341 342 343 344 345 346 347 348 349 350
    return ScopeTypeClosure;
  }
  if (context_->IsCatchContext()) {
    return ScopeTypeCatch;
  }
  if (context_->IsBlockContext()) {
    return ScopeTypeBlock;
  }
  if (context_->IsModuleContext()) {
    return ScopeTypeModule;
  }
  if (context_->IsScriptContext()) {
    return ScopeTypeScript;
  }
351
  DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
352 353 354
  return ScopeTypeWith;
}

355
Handle<JSObject> ScopeIterator::ScopeObject(Mode mode) {
356
  DCHECK(!Done());
357

358
  ScopeType type = Type();
359 360 361 362 363 364 365
  if (type == ScopeTypeGlobal) {
    DCHECK_EQ(Mode::ALL, mode);
    return handle(context_->global_proxy(), isolate_);
  }
  if (type == ScopeTypeWith) {
    DCHECK_EQ(Mode::ALL, mode);
    return WithContextExtension();
366
  }
367

368 369
  Handle<JSObject> scope = isolate_->factory()->NewJSObjectWithNullProto();
  auto visitor = [=](Handle<String> name, Handle<Object> value) {
370
    JSObject::AddProperty(isolate_, scope, name, value, NONE);
371 372
    return false;
  };
373

374 375 376 377 378
  VisitScope(visitor, mode);
  return scope;
}

void ScopeIterator::VisitScope(const Visitor& visitor, Mode mode) const {
379
  switch (Type()) {
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
    case ScopeTypeLocal:
    case ScopeTypeClosure:
    case ScopeTypeCatch:
    case ScopeTypeBlock:
    case ScopeTypeEval:
      return VisitLocalScope(visitor, mode);
    case ScopeTypeModule:
      if (InInnerScope()) {
        return VisitLocalScope(visitor, mode);
      }
      DCHECK_EQ(Mode::ALL, mode);
      return VisitModuleScope(visitor);
    case ScopeTypeScript:
      DCHECK_EQ(Mode::ALL, mode);
      return VisitScriptScope(visitor);
    case ScopeTypeWith:
    case ScopeTypeGlobal:
      UNREACHABLE();
398 399 400
  }
}

401 402
bool ScopeIterator::SetVariableValue(Handle<String> name,
                                     Handle<Object> value) {
403
  DCHECK(!Done());
404 405 406 407 408
  name = isolate_->factory()->InternalizeString(name);
  switch (Type()) {
    case ScopeTypeGlobal:
    case ScopeTypeWith:
      break;
409

410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
    case ScopeTypeEval:
    case ScopeTypeBlock:
    case ScopeTypeCatch:
    case ScopeTypeModule:
      if (InInnerScope()) return SetLocalVariableValue(name, value);
      if (Type() == ScopeTypeModule && SetModuleVariableValue(name, value)) {
        return true;
      }
      return SetContextVariableValue(name, value);

    case ScopeTypeLocal:
    case ScopeTypeClosure:
      if (InInnerScope()) {
        DCHECK_EQ(ScopeTypeLocal, Type());
        if (SetLocalVariableValue(name, value)) return true;
        // There may not be an associated context since we're InInnerScope().
        if (!current_scope_->NeedsContext()) return false;
      } else {
        DCHECK_EQ(ScopeTypeClosure, Type());
        if (SetContextVariableValue(name, value)) return true;
      }
      // The above functions only set variables statically declared in the
      // function. There may be eval-introduced variables. Check them in
      // SetContextExtensionValue.
      return SetContextExtensionValue(name, value);
435

436 437
    case ScopeTypeScript:
      return SetScriptVariableValue(name, value);
438
  }
439
  return false;
440 441
}

442
Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; }
443

444 445 446
#ifdef DEBUG
// Debug print of the content of the current scope.
void ScopeIterator::DebugPrint() {
447
  StdoutStream os;
448
  DCHECK(!Done());
449 450 451
  switch (Type()) {
    case ScopeIterator::ScopeTypeGlobal:
      os << "Global:\n";
452
      context_->Print(os);
453 454 455 456
      break;

    case ScopeIterator::ScopeTypeLocal: {
      os << "Local:\n";
457
      if (current_scope_->NeedsContext()) {
458
        context_->Print(os);
459 460 461
        if (context_->has_extension()) {
          Handle<HeapObject> extension(context_->extension(), isolate_);
          DCHECK(extension->IsJSContextExtensionObject());
462
          extension->Print(os);
463 464 465 466 467 468 469
        }
      }
      break;
    }

    case ScopeIterator::ScopeTypeWith:
      os << "With:\n";
470
      context_->extension()->Print(os);
471 472 473 474
      break;

    case ScopeIterator::ScopeTypeCatch:
      os << "Catch:\n";
475 476
      context_->extension()->Print(os);
      context_->get(Context::THROWN_OBJECT_INDEX)->Print(os);
477 478 479 480
      break;

    case ScopeIterator::ScopeTypeClosure:
      os << "Closure:\n";
481
      context_->Print(os);
482 483 484
      if (context_->has_extension()) {
        Handle<HeapObject> extension(context_->extension(), isolate_);
        DCHECK(extension->IsJSContextExtensionObject());
485
        extension->Print(os);
486 487 488 489 490
      }
      break;

    case ScopeIterator::ScopeTypeScript:
      os << "Script:\n";
491
      context_->global_object()
492 493
          ->native_context()
          ->script_context_table()
494
          ->Print(os);
495 496 497 498 499 500 501 502 503
      break;

    default:
      UNREACHABLE();
  }
  PrintF("\n");
}
#endif

504 505 506 507 508 509 510 511 512
int ScopeIterator::GetSourcePosition() {
  if (frame_inspector_) {
    return frame_inspector_->GetSourcePosition();
  } else {
    DCHECK(!generator_.is_null());
    return generator_->source_position();
  }
}

verwaest's avatar
verwaest committed
513 514
void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) {
  DCHECK_NOT_NULL(scope);
515

516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
  const int position = GetSourcePosition();

  Scope* parent = nullptr;
  Scope* current = scope;
  while (parent != current) {
    parent = current;
    for (Scope* inner_scope = current->inner_scope(); inner_scope != nullptr;
         inner_scope = inner_scope->sibling()) {
      int beg_pos = inner_scope->start_position();
      int end_pos = inner_scope->end_position();
      DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden());
      if (beg_pos <= position && position < end_pos) {
        // Don't walk into inner functions.
        if (!inner_scope->is_function_scope()) {
          current = inner_scope;
        }
        break;
      }
    }
  }
536

537 538 539
  start_scope_ = current;
  current_scope_ = current;
}
540

541 542
void ScopeIterator::VisitScriptScope(const Visitor& visitor) const {
  Handle<JSGlobalObject> global(context_->global_object(), isolate_);
543
  Handle<ScriptContextTable> script_contexts(
544
      global->native_context()->script_context_table(), isolate_);
545

546 547
  // Skip the first script since that just declares 'this'.
  for (int context_index = 1; context_index < script_contexts->used();
548
       context_index++) {
549 550
    Handle<Context> context = ScriptContextTable::GetContext(
        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 557
void ScopeIterator::VisitModuleScope(const Visitor& visitor) const {
  DCHECK(context_->IsModuleContext());
558

559
  Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
560 561 562 563
  if (VisitContextLocals(visitor, scope_info, context_)) return;

  int count_index = scope_info->ModuleVariableCountIndex();
  int module_variable_count = Smi::cast(scope_info->get(count_index))->value();
564

565
  Handle<Module> module(context_->module(), isolate_);
566

567 568 569 570 571 572 573 574
  for (int i = 0; i < module_variable_count; ++i) {
    int index;
    Handle<String> name;
    {
      String* raw_name;
      scope_info->ModuleVariable(i, &raw_name, &index);
      CHECK(!ScopeInfo::VariableIsSynthetic(raw_name));
      name = handle(raw_name, isolate_);
575
    }
576
    Handle<Object> value = Module::LoadVariable(isolate_, module, index);
577

578 579 580
    // Reflect variables under TDZ as undeclared in scope object.
    if (value->IsTheHole(isolate_)) continue;
    if (visitor(name, value)) return;
581
  }
582
}
583

584 585 586 587 588
bool ScopeIterator::VisitContextLocals(const Visitor& visitor,
                                       Handle<ScopeInfo> scope_info,
                                       Handle<Context> context) const {
  // Fill all context locals to the context extension.
  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 592 593 594 595
    int context_index = Context::MIN_CONTEXT_SLOTS + i;
    Handle<Object> value(context->get(context_index), isolate_);
    // Reflect variables under TDZ as undefined in scope object.
    if (value->IsTheHole(isolate_)) continue;
    if (visitor(name, value)) return true;
596
  }
597
  return false;
598
}
599

600 601 602 603 604
bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode) const {
  for (Variable* var : *current_scope_->locals()) {
    if (!var->is_this() && ScopeInfo::VariableIsSynthetic(*var->name())) {
      continue;
    }
605

606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
    int index = var->index();
    Handle<Object> value;
    switch (var->location()) {
      case VariableLocation::LOOKUP:
        UNREACHABLE();
        break;

      case VariableLocation::UNALLOCATED:
        if (!var->is_this()) continue;
        // No idea why we only add it sometimes.
        if (mode == Mode::ALL) continue;
        // No idea why this diverges...
        value = frame_inspector_->GetReceiver();
        break;

      case VariableLocation::PARAMETER: {
        if (frame_inspector_ == nullptr) {
          // Get the variable from the suspended generator.
          DCHECK(!generator_.is_null());
          if (var->is_this()) {
            value = handle(generator_->receiver(), isolate_);
          } else {
            FixedArray* parameters_and_registers =
                generator_->parameters_and_registers();
            DCHECK_LT(index, parameters_and_registers->length());
            value = handle(parameters_and_registers->get(index), isolate_);
          }
        } else {
          value = var->is_this() ? frame_inspector_->GetReceiver()
                                 : frame_inspector_->GetParameter(index);

          if (value->IsOptimizedOut(isolate_)) {
            value = isolate_->factory()->undefined_value();
          } else if (var->is_this() && value->IsTheHole(isolate_)) {
            value = isolate_->factory()->undefined_value();
          }
        }
        break;
      }
645

646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
      case VariableLocation::LOCAL:
        if (frame_inspector_ == nullptr) {
          // Get the variable from the suspended generator.
          DCHECK(!generator_.is_null());
          FixedArray* parameters_and_registers =
              generator_->parameters_and_registers();
          int parameter_count =
              function_->shared()->scope_info()->ParameterCount();
          index += parameter_count;
          DCHECK_LT(index, parameters_and_registers->length());
          value = handle(parameters_and_registers->get(index), isolate_);
          if (value->IsTheHole(isolate_)) {
            value = isolate_->factory()->undefined_value();
          }
        } else {
          value = frame_inspector_->GetExpression(index);
          if (value->IsOptimizedOut(isolate_)) {
            // We'll rematerialize this later.
            if (current_scope_->is_declaration_scope() &&
                current_scope_->AsDeclarationScope()->arguments() == var) {
              continue;
            }
            value = isolate_->factory()->undefined_value();
          } else if (value->IsTheHole(isolate_)) {
            // Reflect variables under TDZ as undeclared in scope object.
            continue;
          }
        }
        break;

      case VariableLocation::CONTEXT:
        if (mode == Mode::STACK) continue;
        // TODO(verwaest): Why don't we want to show it if it's there?...
        if (var->is_this()) continue;
        DCHECK(var->IsContextSlot());
        value = handle(context_->get(index), isolate_);
        // Reflect variables under TDZ as undeclared in scope object.
        if (value->IsTheHole(isolate_)) continue;
        break;

      case VariableLocation::MODULE: {
        if (mode == Mode::STACK) continue;
        // if (var->IsExport()) continue;
        Handle<Module> module(context_->module(), isolate_);
690
        value = Module::LoadVariable(isolate_, module, var->index());
691 692 693 694 695
        // Reflect variables under TDZ as undeclared in scope object.
        if (value->IsTheHole(isolate_)) continue;
        break;
      }
    }
696

697
    if (visitor(var->name(), value)) return true;
698
  }
699
  return false;
700 701
}

702 703 704
// Retrieve the with-context extension object. If the extension object is
// a proxy, return an empty object.
Handle<JSObject> ScopeIterator::WithContextExtension() {
705 706
  DCHECK(context_->IsWithContext());
  if (context_->extension_receiver()->IsJSProxy()) {
707 708
    return isolate_->factory()->NewJSObjectWithNullProto();
  }
709
  return handle(JSObject::cast(context_->extension_receiver()), isolate_);
710
}
711 712 713

// Create a plain JSObject which materializes the block scope for the specified
// block context.
714 715 716 717 718 719 720 721
void ScopeIterator::VisitLocalScope(const Visitor& visitor, Mode mode) const {
  if (InInnerScope()) {
    if (VisitLocals(visitor, mode)) return;
    if (mode == Mode::STACK && Type() == ScopeTypeLocal) {
      // Hide |this| in arrow functions that may be embedded in other functions
      // but don't force |this| to be context-allocated. Otherwise we'd find the
      // wrong |this| value.
      if (!closure_scope_->has_this_declaration() &&
722
          !non_locals_->Has(isolate_, isolate_->factory()->this_string())) {
723 724 725
        if (visitor(isolate_->factory()->this_string(),
                    isolate_->factory()->undefined_value()))
          return;
726
      }
727 728 729 730 731 732 733 734 735
      // Add |arguments| to the function scope even if it wasn't used.
      // Currently we don't yet support materializing the arguments object of
      // suspended generators. We'd need to read the arguments out from the
      // suspended generator rather than from an activation as
      // FunctionGetArguments does.
      if (frame_inspector_ != nullptr && !closure_scope_->is_arrow_scope() &&
          (closure_scope_->arguments() == nullptr ||
           frame_inspector_->GetExpression(closure_scope_->arguments()->index())
               ->IsOptimizedOut(isolate_))) {
736
        JavaScriptFrame* frame = GetFrame();
737 738 739
        Handle<JSObject> arguments = Accessors::FunctionGetArguments(
            frame, frame_inspector_->inlined_frame_index());
        if (visitor(isolate_->factory()->arguments_string(), arguments)) return;
740
      }
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
    }
  } else {
    DCHECK_EQ(Mode::ALL, mode);
    Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
    if (VisitContextLocals(visitor, scope_info, context_)) return;
  }

  if (mode == Mode::ALL && HasContext()) {
    DCHECK(!context_->IsScriptContext());
    DCHECK(!context_->IsNativeContext());
    DCHECK(!context_->IsWithContext());
    if (!context_->scope_info()->CallsSloppyEval()) return;
    if (context_->extension_object() == nullptr) return;
    Handle<JSObject> extension(context_->extension_object(), isolate_);
    Handle<FixedArray> keys =
        KeyAccumulator::GetKeys(extension, KeyCollectionMode::kOwnOnly,
                                ENUMERABLE_STRINGS)
            .ToHandleChecked();

    for (int i = 0; i < keys->length(); i++) {
      // Names of variables introduced by eval are strings.
      DCHECK(keys->get(i)->IsString());
763
      Handle<String> key(String::cast(keys->get(i)), isolate_);
764 765
      Handle<Object> value = JSReceiver::GetDataProperty(extension, key);
      if (visitor(key, value)) return;
766 767
    }
  }
768
}
769

770 771 772 773 774
bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
                                          Handle<Object> new_value) {
  // TODO(verwaest): Walk parameters backwards, not forwards.
  // TODO(verwaest): Use VariableMap rather than locals() list for lookup.
  for (Variable* var : *current_scope_->locals()) {
775
    if (String::Equals(isolate_, var->name(), variable_name)) {
776 777 778 779 780 781
      int index = var->index();
      switch (var->location()) {
        case VariableLocation::LOOKUP:
        case VariableLocation::UNALLOCATED:
          // Drop assignments to unallocated locals.
          DCHECK(var->is_this() ||
782
                 *variable_name == ReadOnlyRoots(isolate_).arguments_string());
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
          return false;

        case VariableLocation::PARAMETER: {
          if (var->is_this()) return false;
          if (frame_inspector_ == nullptr) {
            // Set the variable in the suspended generator.
            DCHECK(!generator_.is_null());
            Handle<FixedArray> parameters_and_registers(
                generator_->parameters_and_registers(), isolate_);
            DCHECK_LT(index, parameters_and_registers->length());
            parameters_and_registers->set(index, *new_value);
          } else {
            JavaScriptFrame* frame = GetFrame();
            if (frame->is_optimized()) return false;

            frame->SetParameterValue(index, *new_value);
          }
          return true;
        }
802

803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
        case VariableLocation::LOCAL:
          if (frame_inspector_ == nullptr) {
            // Set the variable in the suspended generator.
            DCHECK(!generator_.is_null());
            int parameter_count =
                function_->shared()->scope_info()->ParameterCount();
            index += parameter_count;
            Handle<FixedArray> parameters_and_registers(
                generator_->parameters_and_registers(), isolate_);
            DCHECK_LT(index, parameters_and_registers->length());
            parameters_and_registers->set(index, *new_value);
          } else {
            // Set the variable on the stack.
            JavaScriptFrame* frame = GetFrame();
            if (frame->is_optimized()) return false;

            frame->SetExpression(index, *new_value);
          }
          return true;

        case VariableLocation::CONTEXT:
          DCHECK(var->IsContextSlot());
          context_->set(index, *new_value);
          return true;

        case VariableLocation::MODULE:
          if (!var->IsExport()) return false;
          Handle<Module> module(context_->module(), isolate_);
          Module::StoreVariable(module, var->index(), new_value);
          return true;
      }
      UNREACHABLE();
835 836
    }
  }
837

838
  return false;
839
}
840

841 842 843
bool ScopeIterator::SetContextExtensionValue(Handle<String> variable_name,
                                             Handle<Object> new_value) {
  if (!context_->has_extension()) return false;
844

845 846
  DCHECK(context_->extension_object()->IsJSContextExtensionObject());
  Handle<JSObject> ext(context_->extension_object(), isolate_);
847
  LookupIterator it(isolate_, ext, variable_name, LookupIterator::OWN);
848 849 850
  Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
  DCHECK(maybe.IsJust());
  if (!maybe.FromJust()) return false;
851

852 853 854
  CHECK(Object::SetDataProperty(&it, new_value).ToChecked());
  return true;
}
855

856 857 858 859 860 861 862 863 864 865
bool ScopeIterator::SetContextVariableValue(Handle<String> variable_name,
                                            Handle<Object> new_value) {
  Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);

  VariableMode mode;
  InitializationFlag flag;
  MaybeAssignedFlag maybe_assigned_flag;
  int slot_index = ScopeInfo::ContextSlotIndex(scope_info, variable_name, &mode,
                                               &flag, &maybe_assigned_flag);
  if (slot_index < 0) return false;
866

867 868
  context_->set(slot_index, *new_value);
  return true;
869 870
}

871 872 873
bool ScopeIterator::SetModuleVariableValue(Handle<String> variable_name,
                                           Handle<Object> new_value) {
  int cell_index;
874 875 876 877 878
  VariableMode mode;
  InitializationFlag init_flag;
  MaybeAssignedFlag maybe_assigned_flag;
  cell_index = context_->scope_info()->ModuleIndex(
      variable_name, &mode, &init_flag, &maybe_assigned_flag);
879 880

  // Setting imports is currently not supported.
881 882 883
  if (ModuleDescriptor::GetCellIndexKind(cell_index) !=
      ModuleDescriptor::kExport) {
    return false;
884 885
  }

886 887 888
  Handle<Module> module(context_->module(), isolate_);
  Module::StoreVariable(module, cell_index, new_value);
  return true;
889 890 891 892 893
}

bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
                                           Handle<Object> new_value) {
  Handle<ScriptContextTable> script_contexts(
894 895
      context_->global_object()->native_context()->script_context_table(),
      isolate_);
896
  ScriptContextTable::LookupResult lookup_result;
897
  if (ScriptContextTable::Lookup(isolate_, script_contexts, variable_name,
898 899
                                 &lookup_result)) {
    Handle<Context> script_context = ScriptContextTable::GetContext(
900
        isolate_, script_contexts, lookup_result.context_index);
901 902 903 904 905 906 907 908 909
    script_context->set(lookup_result.slot_index, *new_value);
    return true;
  }

  return false;
}

}  // namespace internal
}  // namespace v8