debug-scopes.cc 37.6 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/common/globals.h"
12
#include "src/debug/debug.h"
13 14
#include "src/execution/frames-inl.h"
#include "src/execution/isolate-inl.h"
15
#include "src/objects/js-generator-inl.h"
16
#include "src/objects/source-text-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
#include "src/utils/ostreams.h"
21 22 23 24 25

namespace v8 {
namespace internal {

ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
26
                             ReparseStrategy strategy)
27 28
    : isolate_(isolate),
      frame_inspector_(frame_inspector),
29
      function_(frame_inspector_->GetFunction()),
30
      script_(frame_inspector_->GetScript()) {
31
  if (!frame_inspector->GetContext()->IsContext()) {
32 33 34
    // Optimized frame, context or function cannot be materialized. Give up.
    return;
  }
35
  context_ = Handle<Context>::cast(frame_inspector->GetContext());
36

37
  // We should not instantiate a ScopeIterator for wasm frames.
38
  DCHECK_NE(Script::TYPE_WASM, frame_inspector->GetScript()->type());
39

40
  TryParseAndRetrieveScopes(strategy);
41 42
}

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

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

48 49
  if (!context_->IsNativeContext()) {
    DisallowHeapAllocation no_gc;
50 51
    ScopeInfo closure_info = context_->closure_context().scope_info();
    Handle<String> debug_name(closure_info.FunctionDebugName(), isolate_);
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)
58
    : isolate_(isolate), context_(function->context(), isolate) {
59
  if (!function->shared().IsSubjectToDebugging()) {
60 61 62
    context_ = Handle<Context>();
    return;
  }
63
  script_ = handle(Script::cast(function->shared().script()), isolate);
64 65 66 67 68 69 70
  UnwrapEvaluationContext();
}

ScopeIterator::ScopeIterator(Isolate* isolate,
                             Handle<JSGeneratorObject> generator)
    : isolate_(isolate),
      generator_(generator),
71 72
      function_(generator->function(), isolate),
      context_(generator->context(), isolate),
73 74
      script_(Script::cast(function_->shared().script()), isolate) {
  CHECK(function_->shared().IsSubjectToDebugging());
75
  TryParseAndRetrieveScopes(ReparseStrategy::kFunctionLiteral);
76
}
77

78 79 80 81 82 83 84 85 86
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();
}

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
namespace {

// Takes the scope of a parsed script, a function and a break location
// inside the function. The result is the innermost lexical scope around
// the break point, which serves as the starting point of the ScopeIterator.
// And the scope of the function that was passed in (called closure scope).
//
// The start scope is guaranteed to be either the closure scope itself,
// or a child of the closure scope.
class ScopeChainRetriever {
 public:
  ScopeChainRetriever(DeclarationScope* scope, Handle<JSFunction> function,
                      int position)
      : scope_(scope),
        break_scope_start_(function->shared().StartPosition()),
        break_scope_end_(function->shared().EndPosition()),
        is_default_constructor_(
            IsDefaultConstructor(function->shared().kind())),
        position_(position) {
    DCHECK_NOT_NULL(scope);
    RetrieveScopes();
  }

  DeclarationScope* ClosureScope() { return closure_scope_; }
  Scope* StartScope() { return start_scope_; }

 private:
  DeclarationScope* scope_;
  const int break_scope_start_;
  const int break_scope_end_;
  const bool is_default_constructor_;
  const int position_;

  DeclarationScope* closure_scope_ = nullptr;
  Scope* start_scope_ = nullptr;

  void RetrieveScopes() {
    if (is_default_constructor_) {
      // Even though the DefaultBaseConstructor is a child of a Class scope, the
      // source positions are *not* nested. This means the actual scope for the
      // DefaultBaseConstructor needs to be found by doing a DFS.
      RetrieveScopeChainDefaultConstructor(scope_);
    } else {
      RetrieveScopeChain();
    }
    DCHECK_NOT_NULL(closure_scope_);
    DCHECK_NOT_NULL(start_scope_);
  }

  bool RetrieveScopeChainDefaultConstructor(Scope* scope) {
    const int beg_pos = scope->start_position();
    const int end_pos = scope->end_position();
    if (beg_pos == position_ && end_pos == position_) {
      DCHECK(scope->is_function_scope());
      DCHECK(
          IsDefaultConstructor(scope->AsDeclarationScope()->function_kind()));
      start_scope_ = scope;
      closure_scope_ = scope->AsDeclarationScope();
      return true;
    }

    for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr;
         inner_scope = inner_scope->sibling()) {
      if (RetrieveScopeChainDefaultConstructor(inner_scope)) return true;
    }
    return false;
  }

  void RetrieveScopeChain() {
    Scope* parent = nullptr;
    Scope* current = scope_;
    SetClosureScopeIfFound(current);

    while (parent != current) {
      parent = current;
      for (Scope* inner_scope = current->inner_scope(); inner_scope != nullptr;
           inner_scope = inner_scope->sibling()) {
        if (SetClosureScopeIfFound(inner_scope) ||
            ContainsPosition(inner_scope)) {
          current = inner_scope;
          break;
        }
      }
    }
    start_scope_ = current;
  }

  bool SetClosureScopeIfFound(Scope* scope) {
    const int start = scope->start_position();
    const int end = scope->end_position();
    if (start == break_scope_start_ && end == break_scope_end_) {
      closure_scope_ = scope->AsDeclarationScope();
      return true;
    }
    return false;
  }

  bool ContainsPosition(Scope* scope) {
    const int start = scope->start_position();
    const int end = scope->end_position();
    // In case the closure_scope_ hasn't been found yet, we are less strict
    // about recursing downwards. This might be the case for nested arrow
    // functions that have the same end position.
    const bool position_fits_end =
        closure_scope_ ? position_ < end : position_ <= end;
    return start < position_ && position_fits_end;
  }
};

}  // namespace

198
void ScopeIterator::TryParseAndRetrieveScopes(ReparseStrategy strategy) {
199
  // Catch the case when the debugger stops in an internal function.
200 201
  Handle<SharedFunctionInfo> shared_info(function_->shared(), isolate_);
  Handle<ScopeInfo> scope_info(shared_info->scope_info(), isolate_);
202
  if (shared_info->script().IsUndefined(isolate_)) {
203
    current_scope_ = closure_scope_ = nullptr;
204
    context_ = handle(function_->context(), isolate_);
205
    function_ = Handle<JSFunction>();
206 207 208
    return;
  }

209 210 211
  // Class fields initializer functions don't have any scope
  // information. We short circuit the parsing of the class literal
  // and return an empty context here.
212
  if (IsClassMembersInitializerFunction(shared_info->kind())) {
213 214 215 216 217 218
    current_scope_ = closure_scope_ = nullptr;
    context_ = Handle<Context>();
    function_ = Handle<JSFunction>();
    return;
  }

219 220
  bool ignore_nested_scopes = false;
  if (shared_info->HasBreakInfo() && frame_inspector_ != nullptr) {
221 222 223 224 225 226
    // 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.
227
    Handle<DebugInfo> debug_info(shared_info->GetDebugInfo(), isolate_);
228 229

    // Find the break point where execution has stopped.
230
    BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame());
231 232 233 234

    ignore_nested_scopes = location.IsReturn();
  }

235
  // Reparse the code and analyze the scopes.
236 237
  // Depending on the choosen strategy, the whole script or just
  // the closure is re-parsed for function scopes.
238
  Handle<Script> script(Script::cast(shared_info->script()), isolate_);
239 240
  if (scope_info->scope_type() == FUNCTION_SCOPE &&
      strategy == ReparseStrategy::kFunctionLiteral) {
241
    info_ = new ParseInfo(isolate_, *shared_info);
242
  } else {
243 244
    info_ = new ParseInfo(isolate_, *script);
    info_->set_eager();
245 246
  }

247
  MaybeHandle<ScopeInfo> maybe_outer_scope;
248
  if (scope_info->scope_type() == EVAL_SCOPE || script->is_wrapped()) {
249
    info_->set_eval();
250
    if (!context_->IsNativeContext()) {
251
      maybe_outer_scope = handle(context_->scope_info(), isolate_);
252
    }
253 254
    // Language mode may be inherited from the eval caller.
    // Retrieve it from shared function info.
255
    info_->set_language_mode(shared_info->language_mode());
256
  } else if (scope_info->scope_type() == MODULE_SCOPE) {
257
    DCHECK(info_->is_module());
258 259 260
  } else {
    DCHECK(scope_info->scope_type() == SCRIPT_SCOPE ||
           scope_info->scope_type() == FUNCTION_SCOPE);
261 262
  }

263
  const bool parse_result =
264 265 266
      info_->is_toplevel()
          ? parsing::ParseProgram(info_, script, maybe_outer_scope, isolate_)
          : parsing::ParseFunction(info_, shared_info, isolate_);
267

268
  if (parse_result && Rewriter::Rewrite(info_)) {
269
    info_->ast_value_factory()->Internalize(isolate_);
270 271 272 273 274 275 276 277 278 279 280 281
    DeclarationScope* literal_scope = info_->literal()->scope();

    ScopeChainRetriever scope_chain_retriever(literal_scope, function_,
                                              GetSourcePosition());
    start_scope_ = scope_chain_retriever.StartScope();
    current_scope_ = start_scope_;

    // In case of a FUNCTION_SCOPE, the ScopeIterator expects
    // {closure_scope_} to be set to the scope of the function.
    closure_scope_ = scope_info->scope_type() == FUNCTION_SCOPE
                         ? scope_chain_retriever.ClosureScope()
                         : literal_scope;
282

283
    CHECK(DeclarationScope::Analyze(info_));
284 285 286 287 288
    if (ignore_nested_scopes) {
      current_scope_ = closure_scope_;
      start_scope_ = current_scope_;
      if (closure_scope_->NeedsContext()) {
        context_ = handle(context_->closure_context(), isolate_);
289
      }
verwaest's avatar
verwaest committed
290
    }
291

292
    UnwrapEvaluationContext();
293
  } else {
verwaest's avatar
verwaest committed
294 295 296 297 298 299
    // 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.
300 301 302 303
    // 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
304
  }
305 306
}

307
void ScopeIterator::UnwrapEvaluationContext() {
308
  if (context_->is_null() || !context_->IsDebugEvaluateContext()) return;
309
  Context current = *context_;
310
  do {
311 312
    Object wrapped = current.get(Context::WRAPPED_CONTEXT_INDEX);
    if (wrapped.IsContext()) {
313
      current = Context::cast(wrapped);
314
    } else {
315 316
      DCHECK(!current.previous().is_null());
      current = current.previous();
317
    }
318
  } while (current.IsDebugEvaluateContext());
319
  context_ = handle(current, isolate_);
320 321
}

322
Handle<JSObject> ScopeIterator::MaterializeScopeDetails() {
323 324 325 326 327
  // Calculate the size of the result.
  Handle<FixedArray> details =
      isolate_->factory()->NewFixedArray(kScopeDetailsSize);
  // Fill in scope details.
  details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
328
  Handle<JSObject> scope_object = ScopeObject(Mode::ALL);
329
  details->set(kScopeDetailsObjectIndex, *scope_object);
330 331
  if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
    return isolate_->factory()->NewJSArrayWithElements(details);
332 333 334
  } else if (HasContext()) {
    Handle<Object> closure_name = GetFunctionDebugName();
    details->set(kScopeDetailsNameIndex, *closure_name);
335 336 337
    details->set(kScopeDetailsStartPositionIndex,
                 Smi::FromInt(start_position()));
    details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position()));
338
    if (InInnerScope()) {
339 340
      details->set(kScopeDetailsFunctionIndex, *function_);
    }
341
  }
342 343 344
  return isolate_->factory()->NewJSArrayWithElements(details);
}

345
bool ScopeIterator::HasPositionInfo() {
346
  return InInnerScope() || !context_->IsNativeContext();
347 348 349
}

int ScopeIterator::start_position() {
350
  if (InInnerScope()) return current_scope_->start_position();
351
  if (context_->IsNativeContext()) return 0;
352
  return context_->closure_context().scope_info().StartPosition();
353 354 355
}

int ScopeIterator::end_position() {
356
  if (InInnerScope()) return current_scope_->end_position();
357
  if (context_->IsNativeContext()) return 0;
358
  return context_->closure_context().scope_info().EndPosition();
359
}
360

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
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();
}

380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
void ScopeIterator::AdvanceOneScope() {
  if (current_scope_->NeedsContext()) {
    DCHECK(!context_->previous().is_null());
    context_ = handle(context_->previous(), isolate_);
  }
  DCHECK(current_scope_->outer_scope() != nullptr);
  current_scope_ = current_scope_->outer_scope();
}

void ScopeIterator::AdvanceToNonHiddenScope() {
  do {
    AdvanceOneScope();
  } while (current_scope_->is_hidden());
}

395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
void ScopeIterator::AdvanceContext() {
  DCHECK(!context_->IsNativeContext());
  context_ = handle(context_->previous(), isolate_);

  // While advancing one context, we need to advance at least one
  // scope, but until we hit the next scope that actually requires
  // a context. All the locals collected along the way build the
  // blacklist for debug-evaluate for this context.
  locals_ = StringSet::New(isolate_);
  do {
    if (!current_scope_ || !current_scope_->outer_scope()) break;

    current_scope_ = current_scope_->outer_scope();
    CollectLocalsFromCurrentScope();
  } while (!current_scope_->NeedsContext());
}

412
void ScopeIterator::Next() {
413
  DCHECK(!Done());
414

415
  ScopeType scope_type = Type();
416

417 418 419 420
  if (scope_type == ScopeTypeGlobal) {
    // The global scope is always the last in the chain.
    DCHECK(context_->IsNativeContext());
    context_ = Handle<Context>();
421 422 423 424 425 426 427 428 429
    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());
430 431
    seen_script_scope_ = true;
    if (context_->IsScriptContext()) {
432
      context_ = handle(context_->previous(), isolate_);
433
    }
434
  } else if (!inner) {
435
    AdvanceContext();
436
  } else {
437
    DCHECK_NOT_NULL(current_scope_);
438
    AdvanceToNonHiddenScope();
439 440 441 442 443 444 445

    if (!InInnerScope() && current_scope_ != closure_scope_) {
      // Edge case when we just go past {closure_scope_}. This case
      // already needs to start collecting locals for the blacklist.
      locals_ = StringSet::New(isolate_);
      CollectLocalsFromCurrentScope();
    }
446
  }
447

448
  UnwrapEvaluationContext();
449 450 451
}

// Return the type of the current scope.
452
ScopeIterator::ScopeType ScopeIterator::Type() const {
453
  DCHECK(!Done());
454 455
  if (InInnerScope()) {
    switch (current_scope_->scope_type()) {
456
      case FUNCTION_SCOPE:
457
        DCHECK_IMPLIES(current_scope_->NeedsContext(),
458 459
                       context_->IsFunctionContext() ||
                           context_->IsDebugEvaluateContext());
460 461
        return ScopeTypeLocal;
      case MODULE_SCOPE:
462 463
        DCHECK_IMPLIES(current_scope_->NeedsContext(),
                       context_->IsModuleContext());
464 465
        return ScopeTypeModule;
      case SCRIPT_SCOPE:
466 467 468
        DCHECK_IMPLIES(
            current_scope_->NeedsContext(),
            context_->IsScriptContext() || context_->IsNativeContext());
469 470
        return ScopeTypeScript;
      case WITH_SCOPE:
471 472
        DCHECK_IMPLIES(current_scope_->NeedsContext(),
                       context_->IsWithContext());
473 474 475 476 477
        return ScopeTypeWith;
      case CATCH_SCOPE:
        DCHECK(context_->IsCatchContext());
        return ScopeTypeCatch;
      case BLOCK_SCOPE:
478
      case CLASS_SCOPE:
479 480
        DCHECK_IMPLIES(current_scope_->NeedsContext(),
                       context_->IsBlockContext());
481 482
        return ScopeTypeBlock;
      case EVAL_SCOPE:
483 484
        DCHECK_IMPLIES(current_scope_->NeedsContext(),
                       context_->IsEvalContext());
485
        return ScopeTypeEval;
486
    }
487
    UNREACHABLE();
488 489
  }
  if (context_->IsNativeContext()) {
490
    DCHECK(context_->global_object().IsJSGlobalObject());
491 492 493 494
    // If we are at the native context and have not yet seen script scope,
    // fake it.
    return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
  }
495 496
  if (context_->IsFunctionContext() || context_->IsEvalContext() ||
      context_->IsDebugEvaluateContext()) {
497 498 499 500 501 502 503 504 505 506 507 508 509 510
    return ScopeTypeClosure;
  }
  if (context_->IsCatchContext()) {
    return ScopeTypeCatch;
  }
  if (context_->IsBlockContext()) {
    return ScopeTypeBlock;
  }
  if (context_->IsModuleContext()) {
    return ScopeTypeModule;
  }
  if (context_->IsScriptContext()) {
    return ScopeTypeScript;
  }
511
  DCHECK(context_->IsWithContext());
512 513 514
  return ScopeTypeWith;
}

515
Handle<JSObject> ScopeIterator::ScopeObject(Mode mode) {
516
  DCHECK(!Done());
517

518
  ScopeType type = Type();
519 520 521 522 523 524 525
  if (type == ScopeTypeGlobal) {
    DCHECK_EQ(Mode::ALL, mode);
    return handle(context_->global_proxy(), isolate_);
  }
  if (type == ScopeTypeWith) {
    DCHECK_EQ(Mode::ALL, mode);
    return WithContextExtension();
526
  }
527

528 529
  Handle<JSObject> scope = isolate_->factory()->NewJSObjectWithNullProto();
  auto visitor = [=](Handle<String> name, Handle<Object> value) {
530
    JSObject::AddProperty(isolate_, scope, name, value, NONE);
531 532
    return false;
  };
533

534 535 536 537 538
  VisitScope(visitor, mode);
  return scope;
}

void ScopeIterator::VisitScope(const Visitor& visitor, Mode mode) const {
539
  switch (Type()) {
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
    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();
558 559 560
  }
}

561 562
bool ScopeIterator::SetVariableValue(Handle<String> name,
                                     Handle<Object> value) {
563
  DCHECK(!Done());
564 565 566 567 568
  name = isolate_->factory()->InternalizeString(name);
  switch (Type()) {
    case ScopeTypeGlobal:
    case ScopeTypeWith:
      break;
569

570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
    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);
595

596 597
    case ScopeTypeScript:
      return SetScriptVariableValue(name, value);
598
  }
599
  return false;
600 601
}

602 603 604 605 606
bool ScopeIterator::ClosureScopeHasThisReference() const {
  return !closure_scope_->has_this_declaration() &&
         closure_scope_->HasThisReference();
}

607 608 609 610 611 612 613 614 615 616
void ScopeIterator::CollectLocalsFromCurrentScope() {
  DCHECK(locals_->IsStringSet());
  for (Variable* var : *current_scope_->locals()) {
    if (var->location() == VariableLocation::PARAMETER ||
        var->location() == VariableLocation::LOCAL) {
      locals_ = StringSet::Add(isolate_, locals_, var->name());
    }
  }
}

617 618 619
#ifdef DEBUG
// Debug print of the content of the current scope.
void ScopeIterator::DebugPrint() {
620
  StdoutStream os;
621
  DCHECK(!Done());
622 623 624
  switch (Type()) {
    case ScopeIterator::ScopeTypeGlobal:
      os << "Global:\n";
625
      context_->Print(os);
626 627 628 629
      break;

    case ScopeIterator::ScopeTypeLocal: {
      os << "Local:\n";
630
      if (current_scope_->NeedsContext()) {
631
        context_->Print(os);
632 633 634
        if (context_->has_extension()) {
          Handle<HeapObject> extension(context_->extension(), isolate_);
          DCHECK(extension->IsJSContextExtensionObject());
635
          extension->Print(os);
636 637 638 639 640 641 642
        }
      }
      break;
    }

    case ScopeIterator::ScopeTypeWith:
      os << "With:\n";
643
      context_->extension().Print(os);
644 645 646 647
      break;

    case ScopeIterator::ScopeTypeCatch:
      os << "Catch:\n";
648 649
      context_->extension().Print(os);
      context_->get(Context::THROWN_OBJECT_INDEX).Print(os);
650 651 652 653
      break;

    case ScopeIterator::ScopeTypeClosure:
      os << "Closure:\n";
654
      context_->Print(os);
655 656 657
      if (context_->has_extension()) {
        Handle<HeapObject> extension(context_->extension(), isolate_);
        DCHECK(extension->IsJSContextExtensionObject());
658
        extension->Print(os);
659 660 661 662 663
      }
      break;

    case ScopeIterator::ScopeTypeScript:
      os << "Script:\n";
664 665
      context_->global_object().native_context().script_context_table().Print(
          os);
666 667 668 669 670 671 672 673 674
      break;

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

675 676 677 678 679
int ScopeIterator::GetSourcePosition() {
  if (frame_inspector_) {
    return frame_inspector_->GetSourcePosition();
  } else {
    DCHECK(!generator_.is_null());
680
    SharedFunctionInfo::EnsureSourcePositionsAvailable(
681
        isolate_, handle(generator_->function().shared(), isolate_));
682
    return generator_->source_position();
683 684 685
  }
}

686 687
void ScopeIterator::VisitScriptScope(const Visitor& visitor) const {
  Handle<JSGlobalObject> global(context_->global_object(), isolate_);
688
  Handle<ScriptContextTable> script_contexts(
689
      global->native_context().script_context_table(), isolate_);
690

691 692
  // Skip the first script since that just declares 'this'.
  for (int context_index = 1; context_index < script_contexts->used();
693
       context_index++) {
694 695
    Handle<Context> context = ScriptContextTable::GetContext(
        isolate_, script_contexts, context_index);
696
    Handle<ScopeInfo> scope_info(context->scope_info(), isolate_);
697
    if (VisitContextLocals(visitor, scope_info, context)) return;
698 699 700
  }
}

701 702
void ScopeIterator::VisitModuleScope(const Visitor& visitor) const {
  DCHECK(context_->IsModuleContext());
703

704
  Handle<ScopeInfo> scope_info(context_->scope_info(), isolate_);
705 706 707
  if (VisitContextLocals(visitor, scope_info, context_)) return;

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

710
  Handle<SourceTextModule> module(context_->module(), isolate_);
711

712 713 714 715
  for (int i = 0; i < module_variable_count; ++i) {
    int index;
    Handle<String> name;
    {
716
      String raw_name;
717
      scope_info->ModuleVariable(i, &raw_name, &index);
718
      if (ScopeInfo::VariableIsSynthetic(raw_name)) continue;
719
      name = handle(raw_name, isolate_);
720
    }
721 722
    Handle<Object> value =
        SourceTextModule::LoadVariable(isolate_, module, index);
723

724 725 726
    // Reflect variables under TDZ as undeclared in scope object.
    if (value->IsTheHole(isolate_)) continue;
    if (visitor(name, value)) return;
727
  }
728
}
729

730 731 732 733 734
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) {
735
    Handle<String> name(scope_info->ContextLocalName(i), isolate_);
736
    if (ScopeInfo::VariableIsSynthetic(*name)) continue;
737
    int context_index = scope_info->ContextHeaderLength() + i;
738 739 740 741
    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;
742
  }
743
  return false;
744
}
745

746
bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode) const {
747 748
  if (mode == Mode::STACK && current_scope_->is_declaration_scope() &&
      current_scope_->AsDeclarationScope()->has_this_declaration()) {
749 750 751 752 753 754 755 756 757 758
    // TODO(bmeurer): We should refactor the general variable lookup
    // around "this", since the current way is rather hacky when the
    // receiver is context-allocated.
    auto this_var = current_scope_->AsDeclarationScope()->receiver();
    Handle<Object> receiver =
        this_var->location() == VariableLocation::CONTEXT
            ? handle(context_->get(this_var->index()), isolate_)
            : frame_inspector_ == nullptr
                  ? handle(generator_->receiver(), isolate_)
                  : frame_inspector_->GetReceiver();
759 760
    if (receiver->IsOptimizedOut(isolate_) || receiver->IsTheHole(isolate_)) {
      receiver = isolate_->factory()->undefined_value();
761
    }
762 763 764
    if (visitor(isolate_->factory()->this_string(), receiver)) return true;
  }

765 766 767 768 769 770 771 772 773 774
  if (current_scope_->is_function_scope()) {
    Variable* function_var =
        current_scope_->AsDeclarationScope()->function_var();
    if (function_var != nullptr) {
      Handle<JSFunction> function = frame_inspector_->GetFunction();
      Handle<String> name = function_var->name();
      if (visitor(name, function)) return true;
    }
  }

775 776
  for (Variable* var : *current_scope_->locals()) {
    DCHECK(!var->is_this());
777
    if (ScopeInfo::VariableIsSynthetic(*var->name())) continue;
778

779 780 781 782 783 784 785
    int index = var->index();
    Handle<Object> value;
    switch (var->location()) {
      case VariableLocation::LOOKUP:
        UNREACHABLE();
        break;

Simon Zünd's avatar
Simon Zünd committed
786 787
      case VariableLocation::REPL_GLOBAL:
        // REPL declared variables are ignored for now.
788
      case VariableLocation::UNALLOCATED:
789
        continue;
790 791 792 793 794

      case VariableLocation::PARAMETER: {
        if (frame_inspector_ == nullptr) {
          // Get the variable from the suspended generator.
          DCHECK(!generator_.is_null());
795 796
          FixedArray parameters_and_registers =
              generator_->parameters_and_registers();
797 798
          DCHECK_LT(index, parameters_and_registers.length());
          value = handle(parameters_and_registers.get(index), isolate_);
799
        } else {
800
          value = frame_inspector_->GetParameter(index);
801 802 803 804 805 806 807

          if (value->IsOptimizedOut(isolate_)) {
            value = isolate_->factory()->undefined_value();
          }
        }
        break;
      }
808

809 810 811 812
      case VariableLocation::LOCAL:
        if (frame_inspector_ == nullptr) {
          // Get the variable from the suspended generator.
          DCHECK(!generator_.is_null());
813
          FixedArray parameters_and_registers =
814 815
              generator_->parameters_and_registers();
          int parameter_count =
816
              function_->shared().scope_info().ParameterCount();
817
          index += parameter_count;
818 819
          DCHECK_LT(index, parameters_and_registers.length());
          value = handle(parameters_and_registers.get(index), isolate_);
820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
          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;
        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;
850 851
        Handle<SourceTextModule> module(context_->module(), isolate_);
        value = SourceTextModule::LoadVariable(isolate_, module, var->index());
852 853 854 855 856
        // Reflect variables under TDZ as undeclared in scope object.
        if (value->IsTheHole(isolate_)) continue;
        break;
      }
    }
857

858
    if (visitor(var->name(), value)) return true;
859
  }
860
  return false;
861 862
}

863 864 865
// Retrieve the with-context extension object. If the extension object is
// a proxy, return an empty object.
Handle<JSObject> ScopeIterator::WithContextExtension() {
866
  DCHECK(context_->IsWithContext());
867
  if (context_->extension_receiver().IsJSProxy()) {
868 869
    return isolate_->factory()->NewJSObjectWithNullProto();
  }
870
  return handle(JSObject::cast(context_->extension_receiver()), isolate_);
871
}
872 873 874

// Create a plain JSObject which materializes the block scope for the specified
// block context.
875 876 877 878 879 880 881 882
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() &&
883
          !closure_scope_->HasThisReference()) {
884 885 886
        if (visitor(isolate_->factory()->this_string(),
                    isolate_->factory()->undefined_value()))
          return;
887
      }
888 889 890 891 892 893 894 895 896
      // 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_))) {
897
        JavaScriptFrame* frame = GetFrame();
898 899 900
        Handle<JSObject> arguments = Accessors::FunctionGetArguments(
            frame, frame_inspector_->inlined_frame_index());
        if (visitor(isolate_->factory()->arguments_string(), arguments)) return;
901
      }
902 903 904 905 906 907 908 909 910 911 912
    }
  } 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());
913
    if (!context_->scope_info().SloppyEvalCanExtendVars()) return;
914
    if (context_->extension_object().is_null()) return;
915 916 917 918 919 920 921 922
    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.
923
      DCHECK(keys->get(i).IsString());
924
      Handle<String> key(String::cast(keys->get(i)), isolate_);
925 926
      Handle<Object> value = JSReceiver::GetDataProperty(extension, key);
      if (visitor(key, value)) return;
927 928
    }
  }
929
}
930

931 932 933 934 935
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()) {
936
    if (String::Equals(isolate_, var->name(), variable_name)) {
937 938 939 940 941 942
      int index = var->index();
      switch (var->location()) {
        case VariableLocation::LOOKUP:
        case VariableLocation::UNALLOCATED:
          // Drop assignments to unallocated locals.
          DCHECK(var->is_this() ||
943
                 *variable_name == ReadOnlyRoots(isolate_).arguments_string());
944 945
          return false;

Simon Zünd's avatar
Simon Zünd committed
946 947 948 949
        case VariableLocation::REPL_GLOBAL:
          // Assignments to REPL declared variables are ignored for now.
          return false;

950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966
        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;
        }
967

968 969 970 971 972
        case VariableLocation::LOCAL:
          if (frame_inspector_ == nullptr) {
            // Set the variable in the suspended generator.
            DCHECK(!generator_.is_null());
            int parameter_count =
973
                function_->shared().scope_info().ParameterCount();
974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
            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;
995 996
          Handle<SourceTextModule> module(context_->module(), isolate_);
          SourceTextModule::StoreVariable(module, var->index(), new_value);
997 998 999
          return true;
      }
      UNREACHABLE();
1000 1001
    }
  }
1002

1003
  return false;
1004
}
1005

1006 1007 1008
bool ScopeIterator::SetContextExtensionValue(Handle<String> variable_name,
                                             Handle<Object> new_value) {
  if (!context_->has_extension()) return false;
1009

1010
  DCHECK(context_->extension_object().IsJSContextExtensionObject());
1011
  Handle<JSObject> ext(context_->extension_object(), isolate_);
1012
  LookupIterator it(isolate_, ext, variable_name, LookupIterator::OWN);
1013
  Maybe<bool> maybe = JSReceiver::HasProperty(&it);
1014 1015
  DCHECK(maybe.IsJust());
  if (!maybe.FromJust()) return false;
1016

1017 1018 1019
  CHECK(Object::SetDataProperty(&it, new_value).ToChecked());
  return true;
}
1020

1021 1022
bool ScopeIterator::SetContextVariableValue(Handle<String> variable_name,
                                            Handle<Object> new_value) {
1023
  DisallowHeapAllocation no_gc;
1024 1025 1026
  VariableMode mode;
  InitializationFlag flag;
  MaybeAssignedFlag maybe_assigned_flag;
1027
  IsStaticFlag is_static_flag;
1028 1029
  int slot_index =
      ScopeInfo::ContextSlotIndex(context_->scope_info(), *variable_name, &mode,
1030
                                  &flag, &maybe_assigned_flag, &is_static_flag);
1031
  if (slot_index < 0) return false;
1032

1033 1034
  context_->set(slot_index, *new_value);
  return true;
1035 1036
}

1037 1038
bool ScopeIterator::SetModuleVariableValue(Handle<String> variable_name,
                                           Handle<Object> new_value) {
1039
  DisallowHeapAllocation no_gc;
1040
  int cell_index;
1041 1042 1043
  VariableMode mode;
  InitializationFlag init_flag;
  MaybeAssignedFlag maybe_assigned_flag;
1044
  cell_index = context_->scope_info().ModuleIndex(
1045
      *variable_name, &mode, &init_flag, &maybe_assigned_flag);
1046 1047

  // Setting imports is currently not supported.
1048 1049
  if (SourceTextModuleDescriptor::GetCellIndexKind(cell_index) !=
      SourceTextModuleDescriptor::kExport) {
1050
    return false;
1051 1052
  }

1053 1054
  Handle<SourceTextModule> module(context_->module(), isolate_);
  SourceTextModule::StoreVariable(module, cell_index, new_value);
1055
  return true;
1056 1057 1058 1059 1060
}

bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
                                           Handle<Object> new_value) {
  Handle<ScriptContextTable> script_contexts(
1061
      context_->global_object().native_context().script_context_table(),
1062
      isolate_);
1063
  ScriptContextTable::LookupResult lookup_result;
1064
  if (ScriptContextTable::Lookup(isolate_, *script_contexts, *variable_name,
1065 1066
                                 &lookup_result)) {
    Handle<Context> script_context = ScriptContextTable::GetContext(
1067
        isolate_, script_contexts, lookup_result.context_index);
1068 1069 1070 1071 1072 1073 1074 1075 1076
    script_context->set(lookup_result.slot_index, *new_value);
    return true;
  }

  return false;
}

}  // namespace internal
}  // namespace v8