debug-scopes.cc 35.1 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/parsing/parse-info.h"
16
#include "src/parsing/parsing.h"
verwaest's avatar
verwaest committed
17
#include "src/parsing/rewriter.h"
18 19 20 21 22

namespace v8 {
namespace internal {

ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
23
                             ScopeIterator::Option option)
24 25
    : isolate_(isolate),
      frame_inspector_(frame_inspector),
26
      seen_script_scope_(false) {
27
  if (!frame_inspector->GetContext()->IsContext()) {
28 29 30 31
    // Optimized frame, context or function cannot be materialized. Give up.
    return;
  }

32 33 34
  // We should not instantiate a ScopeIterator for wasm frames.
  DCHECK(frame_inspector->GetScript()->type() != Script::TYPE_WASM);

35 36 37 38 39 40
  TryParseAndRetrieveScopes(option);
}

void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
  context_ = GetContext();

41 42 43 44
  // Catch the case when the debugger stops in an internal function.
  Handle<JSFunction> function = GetFunction();
  Handle<SharedFunctionInfo> shared_info(function->shared());
  Handle<ScopeInfo> scope_info(shared_info->scope_info());
45
  if (shared_info->script()->IsUndefined(isolate_)) {
46 47 48 49 50 51 52 53 54 55 56 57
    while (context_->closure() == *function) {
      context_ = Handle<Context>(context_->previous(), isolate_);
    }
    return;
  }

  // Currently it takes too much time to find nested scopes due to script
  // parsing. Sometimes we want to run the ScopeIterator as fast as possible
  // (for example, while collecting async call stacks on every
  // addEventListener call), even if we drop some nested scopes.
  // Later we may optimize getting the nested scopes (cache the result?)
  // and include nested scopes into the "fast" iteration case as well.
58 59
  bool ignore_nested_scopes = (option == IGNORE_NESTED_SCOPES);
  bool collect_non_locals = (option == COLLECT_NON_LOCALS);
60
  if (!ignore_nested_scopes && shared_info->HasBreakInfo() &&
61
      frame_inspector_ != nullptr) {
62 63 64 65 66 67
    // 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.
68
    Handle<DebugInfo> debug_info(shared_info->GetDebugInfo());
69 70

    // Find the break point where execution has stopped.
71
    BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame());
72 73 74 75 76 77 78 79 80 81 82 83

    ignore_nested_scopes = location.IsReturn();
  }

  if (ignore_nested_scopes) {
    if (scope_info->HasContext()) {
      context_ = Handle<Context>(context_->declaration_context(), isolate_);
    } else {
      while (context_->closure() == *function) {
        context_ = Handle<Context>(context_->previous(), isolate_);
      }
    }
84
    if (scope_info->scope_type() == FUNCTION_SCOPE) {
85 86 87
      nested_scope_chain_.emplace_back(scope_info,
                                       shared_info->start_position(),
                                       shared_info->end_position());
88
    }
89 90 91 92 93
    if (!collect_non_locals) return;
  }

  // Reparse the code and analyze the scopes.
  // Check whether we are in global, eval or function code.
94
  std::unique_ptr<ParseInfo> info;
95 96
  if (scope_info->scope_type() != FUNCTION_SCOPE) {
    // Global or eval code.
97
    Handle<Script> script(Script::cast(shared_info->script()));
98
    info.reset(new ParseInfo(script));
99
    if (scope_info->scope_type() == EVAL_SCOPE) {
100
      info->set_eval();
101 102 103
      if (!function->context()->IsNativeContext()) {
        info->set_outer_scope_info(handle(function->context()->scope_info()));
      }
104 105 106
      // Language mode may be inherited from the eval caller.
      // Retrieve it from shared function info.
      info->set_language_mode(shared_info->language_mode());
107
    } else if (scope_info->scope_type() == MODULE_SCOPE) {
108
      DCHECK(info->is_module());
109 110
    } else {
      DCHECK(scope_info->scope_type() == SCRIPT_SCOPE);
111 112
    }
  } else {
113
    // Inner function.
114
    info.reset(new ParseInfo(shared_info));
115
  }
116
  if (parsing::ParseAny(info.get(), shared_info, isolate_) &&
117 118
      Rewriter::Rewrite(info.get())) {
    info->ast_value_factory()->Internalize(isolate_);
verwaest's avatar
verwaest committed
119 120 121 122 123
    DeclarationScope* scope = info->literal()->scope();
    if (!ignore_nested_scopes || collect_non_locals) {
      CollectNonLocals(info.get(), scope);
    }
    if (!ignore_nested_scopes) {
124
      DeclarationScope::Analyze(info.get());
125 126
      DeclarationScope::AllocateScopeInfos(info.get(), isolate_,
                                           AnalyzeMode::kDebugger);
verwaest's avatar
verwaest committed
127 128
      RetrieveScopeChain(scope);
    }
129
  } else {
verwaest's avatar
verwaest committed
130 131 132 133 134 135
    // 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.
136 137 138 139
    // 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
140
  }
141
  UnwrapEvaluationContext();
142 143 144 145 146
}

ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
    : isolate_(isolate),
      context_(function->context()),
147
      seen_script_scope_(false) {
148
  if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>();
149 150 151
  UnwrapEvaluationContext();
}

152 153 154
ScopeIterator::ScopeIterator(Isolate* isolate,
                             Handle<JSGeneratorObject> generator)
    : isolate_(isolate),
155
      generator_(generator),
156
      context_(generator->context()),
157
      seen_script_scope_(false) {
158 159
  if (!generator->function()->shared()->IsSubjectToDebugging()) {
    context_ = Handle<Context>();
160
    return;
161
  }
162
  TryParseAndRetrieveScopes(DEFAULT);
163 164
}

165 166 167 168 169 170 171 172 173 174 175 176
void ScopeIterator::UnwrapEvaluationContext() {
  while (true) {
    if (context_.is_null()) return;
    if (!context_->IsDebugEvaluateContext()) return;
    Handle<Object> wrapped(context_->get(Context::WRAPPED_CONTEXT_INDEX),
                           isolate_);
    if (wrapped->IsContext()) {
      context_ = Handle<Context>::cast(wrapped);
    } else {
      context_ = Handle<Context>(context_->previous(), isolate_);
    }
  }
177 178 179 180 181 182 183 184 185 186 187 188
}


MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() {
  // Calculate the size of the result.
  Handle<FixedArray> details =
      isolate_->factory()->NewFixedArray(kScopeDetailsSize);
  // Fill in scope details.
  details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
  Handle<JSObject> scope_object;
  ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
  details->set(kScopeDetailsObjectIndex, *scope_object);
189 190 191 192
  if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
    return isolate_->factory()->NewJSArrayWithElements(details);
  }

193
  Handle<JSFunction> js_function = GetClosure();
194
  if (!js_function.is_null()) {
195 196 197 198
    Handle<String> closure_name = JSFunction::GetDebugName(js_function);
    if (!closure_name.is_null() && closure_name->length() != 0) {
      details->set(kScopeDetailsNameIndex, *closure_name);
    }
199 200 201
    details->set(kScopeDetailsStartPositionIndex,
                 Smi::FromInt(start_position()));
    details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position()));
202 203
    details->set(kScopeDetailsFunctionIndex, *js_function);
  }
204 205 206
  return isolate_->factory()->NewJSArrayWithElements(details);
}

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
Handle<JSFunction> ScopeIterator::GetClosure() {
  if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript)
    return Handle<JSFunction>::null();
  if (HasNestedScopeChain()) return GetFunction();
  return HasContext() ? handle(CurrentContext()->closure())
                      : Handle<JSFunction>::null();
}

int ScopeIterator::start_position() {
  if (HasNestedScopeChain()) {
    return LastNestedScopeChain().start_position;
  }
  if (!HasContext()) return 0;
  Handle<JSFunction> js_function = handle(CurrentContext()->closure());
  return js_function.is_null() ? 0 : js_function->shared()->start_position();
}

int ScopeIterator::end_position() {
  if (HasNestedScopeChain()) {
    return LastNestedScopeChain().end_position;
  }
  if (!HasContext()) return 0;
  Handle<JSFunction> js_function = handle(CurrentContext()->closure());
  return js_function.is_null() ? 0 : js_function->shared()->end_position();
}
232 233

void ScopeIterator::Next() {
234
  DCHECK(!Done());
235 236 237 238 239
  ScopeType scope_type = Type();
  if (scope_type == ScopeTypeGlobal) {
    // The global scope is always the last in the chain.
    DCHECK(context_->IsNativeContext());
    context_ = Handle<Context>();
240
  } else if (scope_type == ScopeTypeScript) {
241 242 243 244
    seen_script_scope_ = true;
    if (context_->IsScriptContext()) {
      context_ = Handle<Context>(context_->previous(), isolate_);
    }
245 246
    if (HasNestedScopeChain()) {
      DCHECK_EQ(LastNestedScopeChain().scope_info->scope_type(), SCRIPT_SCOPE);
247
      nested_scope_chain_.pop_back();
248
      DCHECK(!HasNestedScopeChain());
249 250
    }
    CHECK(context_->IsNativeContext());
251
  } else if (!HasNestedScopeChain()) {
252 253
    context_ = Handle<Context>(context_->previous(), isolate_);
  } else {
254
    do {
255
      if (LastNestedScopeChain().scope_info->HasContext()) {
256
        DCHECK(context_->previous() != nullptr);
257 258
        context_ = Handle<Context>(context_->previous(), isolate_);
      }
259
      nested_scope_chain_.pop_back();
260
      if (!HasNestedScopeChain()) break;
261
      // Repeat to skip hidden scopes.
262
    } while (LastNestedScopeChain().is_hidden());
263
  }
264
  UnwrapEvaluationContext();
265 266 267 268 269
}


// Return the type of the current scope.
ScopeIterator::ScopeType ScopeIterator::Type() {
270
  DCHECK(!Done());
271 272
  if (HasNestedScopeChain()) {
    Handle<ScopeInfo> scope_info = LastNestedScopeChain().scope_info;
273 274 275 276 277 278 279 280 281 282 283
    switch (scope_info->scope_type()) {
      case FUNCTION_SCOPE:
        DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
        return ScopeTypeLocal;
      case MODULE_SCOPE:
        DCHECK(context_->IsModuleContext());
        return ScopeTypeModule;
      case SCRIPT_SCOPE:
        DCHECK(context_->IsScriptContext() || context_->IsNativeContext());
        return ScopeTypeScript;
      case WITH_SCOPE:
284
        DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
285 286 287 288 289 290 291 292
        return ScopeTypeWith;
      case CATCH_SCOPE:
        DCHECK(context_->IsCatchContext());
        return ScopeTypeCatch;
      case BLOCK_SCOPE:
        DCHECK(!scope_info->HasContext() || context_->IsBlockContext());
        return ScopeTypeBlock;
      case EVAL_SCOPE:
293
        DCHECK(!scope_info->HasContext() || context_->IsEvalContext());
294
        return ScopeTypeEval;
295
    }
296
    UNREACHABLE();
297 298
  }
  if (context_->IsNativeContext()) {
299
    DCHECK(context_->global_object()->IsJSGlobalObject());
300 301 302 303
    // If we are at the native context and have not yet seen script scope,
    // fake it.
    return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
  }
304
  if (context_->IsFunctionContext() || context_->IsEvalContext()) {
305 306 307 308 309 310 311 312 313 314 315 316 317 318
    return ScopeTypeClosure;
  }
  if (context_->IsCatchContext()) {
    return ScopeTypeCatch;
  }
  if (context_->IsBlockContext()) {
    return ScopeTypeBlock;
  }
  if (context_->IsModuleContext()) {
    return ScopeTypeModule;
  }
  if (context_->IsScriptContext()) {
    return ScopeTypeScript;
  }
319
  DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
320 321 322 323 324
  return ScopeTypeWith;
}


MaybeHandle<JSObject> ScopeIterator::ScopeObject() {
325
  DCHECK(!Done());
326 327
  switch (Type()) {
    case ScopeIterator::ScopeTypeGlobal:
328
      return Handle<JSObject>(CurrentContext()->global_proxy());
329 330 331 332
    case ScopeIterator::ScopeTypeScript:
      return MaterializeScriptScope();
    case ScopeIterator::ScopeTypeLocal:
      // Materialize the content of the local scope into a JSObject.
333
      DCHECK_EQ(1, nested_scope_chain_.size());
334 335
      return MaterializeLocalScope();
    case ScopeIterator::ScopeTypeWith:
336
      return WithContextExtension();
337 338 339 340 341 342
    case ScopeIterator::ScopeTypeCatch:
      return MaterializeCatchScope();
    case ScopeIterator::ScopeTypeClosure:
      // Materialize the content of the closure scope into a JSObject.
      return MaterializeClosure();
    case ScopeIterator::ScopeTypeBlock:
343 344
    case ScopeIterator::ScopeTypeEval:
      return MaterializeInnerScope();
345 346 347 348 349 350 351 352 353
    case ScopeIterator::ScopeTypeModule:
      return MaterializeModuleScope();
  }
  UNREACHABLE();
}


bool ScopeIterator::HasContext() {
  ScopeType type = Type();
354 355
  if (type == ScopeTypeBlock || type == ScopeTypeLocal ||
      type == ScopeTypeEval) {
356 357
    if (HasNestedScopeChain()) {
      return LastNestedScopeChain().scope_info->HasContext();
358 359 360 361 362 363 364 365
    }
  }
  return true;
}


bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
                                     Handle<Object> new_value) {
366
  DCHECK(!Done());
367 368 369 370 371 372 373 374 375 376 377 378 379 380
  switch (Type()) {
    case ScopeIterator::ScopeTypeGlobal:
      break;
    case ScopeIterator::ScopeTypeLocal:
      return SetLocalVariableValue(variable_name, new_value);
    case ScopeIterator::ScopeTypeWith:
      break;
    case ScopeIterator::ScopeTypeCatch:
      return SetCatchVariableValue(variable_name, new_value);
    case ScopeIterator::ScopeTypeClosure:
      return SetClosureVariableValue(variable_name, new_value);
    case ScopeIterator::ScopeTypeScript:
      return SetScriptVariableValue(variable_name, new_value);
    case ScopeIterator::ScopeTypeBlock:
381 382
    case ScopeIterator::ScopeTypeEval:
      return SetInnerScopeVariableValue(variable_name, new_value);
383
    case ScopeIterator::ScopeTypeModule:
384
      return SetModuleVariableValue(variable_name, new_value);
385 386 387 388 389 390 391
      break;
  }
  return false;
}


Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
392
  DCHECK(!Done());
393 394
  if (HasNestedScopeChain()) {
    return LastNestedScopeChain().scope_info;
395 396
  } else if (context_->IsBlockContext() || context_->IsFunctionContext() ||
             context_->IsEvalContext()) {
397
    return Handle<ScopeInfo>(context_->scope_info());
398 399 400 401 402 403
  }
  return Handle<ScopeInfo>::null();
}


Handle<Context> ScopeIterator::CurrentContext() {
404
  DCHECK(!Done());
405
  if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
406
      !HasNestedScopeChain()) {
407
    return context_;
408
  } else if (LastNestedScopeChain().scope_info->HasContext()) {
409 410
    return context_;
  } else {
411
    return Handle<Context>::null();
412 413 414
  }
}

415
Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; }
416

417 418 419 420
#ifdef DEBUG
// Debug print of the content of the current scope.
void ScopeIterator::DebugPrint() {
  OFStream os(stdout);
421
  DCHECK(!Done());
422 423 424 425 426 427 428 429 430 431 432 433
  switch (Type()) {
    case ScopeIterator::ScopeTypeGlobal:
      os << "Global:\n";
      CurrentContext()->Print(os);
      break;

    case ScopeIterator::ScopeTypeLocal: {
      os << "Local:\n";
      GetFunction()->shared()->scope_info()->Print();
      if (!CurrentContext().is_null()) {
        CurrentContext()->Print(os);
        if (CurrentContext()->has_extension()) {
434
          Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
          if (extension->IsJSContextExtensionObject()) {
            extension->Print(os);
          }
        }
      }
      break;
    }

    case ScopeIterator::ScopeTypeWith:
      os << "With:\n";
      CurrentContext()->extension()->Print(os);
      break;

    case ScopeIterator::ScopeTypeCatch:
      os << "Catch:\n";
      CurrentContext()->extension()->Print(os);
      CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os);
      break;

    case ScopeIterator::ScopeTypeClosure:
      os << "Closure:\n";
      CurrentContext()->Print(os);
      if (CurrentContext()->has_extension()) {
458
        Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
        if (extension->IsJSContextExtensionObject()) {
          extension->Print(os);
        }
      }
      break;

    case ScopeIterator::ScopeTypeScript:
      os << "Script:\n";
      CurrentContext()
          ->global_object()
          ->native_context()
          ->script_context_table()
          ->Print(os);
      break;

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

481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
inline Handle<Context> ScopeIterator::GetContext() {
  if (frame_inspector_) {
    return Handle<Context>::cast(frame_inspector_->GetContext());
  } else {
    DCHECK(!generator_.is_null());
    return handle(generator_->context());
  }
}

Handle<JSFunction> ScopeIterator::GetFunction() {
  if (frame_inspector_) {
    return frame_inspector_->GetFunction();
  } else {
    DCHECK(!generator_.is_null());
    return handle(generator_->function());
  }
}

int ScopeIterator::GetSourcePosition() {
  if (frame_inspector_) {
    return frame_inspector_->GetSourcePosition();
  } else {
    DCHECK(!generator_.is_null());
    return generator_->source_position();
  }
}

verwaest's avatar
verwaest committed
508 509
void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) {
  DCHECK_NOT_NULL(scope);
510
  GetNestedScopeChain(isolate_, scope, GetSourcePosition());
511 512
}

verwaest's avatar
verwaest committed
513 514 515 516
void ScopeIterator::CollectNonLocals(ParseInfo* info, DeclarationScope* scope) {
  DCHECK_NOT_NULL(scope);
  DCHECK(non_locals_.is_null());
  non_locals_ = scope->CollectNonLocals(info, StringSet::New(isolate_));
517 518 519
}


520
MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() {
521
  Handle<JSGlobalObject> global(CurrentContext()->global_object());
522 523 524 525
  Handle<ScriptContextTable> script_contexts(
      global->native_context()->script_context_table());

  Handle<JSObject> script_scope =
526
      isolate_->factory()->NewJSObjectWithNullProto();
527 528 529 530 531

  for (int context_index = 0; context_index < script_contexts->used();
       context_index++) {
    Handle<Context> context =
        ScriptContextTable::GetContext(script_contexts, context_index);
532
    Handle<ScopeInfo> scope_info(context->scope_info());
533 534 535 536 537
    CopyContextLocalsToScopeObject(scope_info, context, script_scope);
  }
  return script_scope;
}

538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
void ScopeIterator::MaterializeStackLocals(Handle<JSObject> local_scope,
                                           Handle<ScopeInfo> scope_info) {
  if (frame_inspector_) {
    return frame_inspector_->MaterializeStackLocals(local_scope, scope_info);
  }

  DCHECK(!generator_.is_null());
  // Fill all stack locals.
  Handle<FixedArray> register_file(generator_->register_file());
  for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
    Handle<String> name = handle(scope_info->StackLocalName(i));
    if (ScopeInfo::VariableIsSynthetic(*name)) continue;
    Handle<Object> value(register_file->get(scope_info->StackLocalIndex(i)),
                         isolate_);
    // TODO(yangguo): We convert optimized out values to {undefined} when they
    // are passed to the debugger. Eventually we should handle them somehow.
    if (value->IsTheHole(isolate_) || value->IsOptimizedOut(isolate_)) {
      DCHECK(!value.is_identical_to(isolate_->factory()->stale_register()));
      value = isolate_->factory()->undefined_value();
    }
    JSObject::SetOwnPropertyIgnoreAttributes(local_scope, name, value, NONE)
        .Check();
  }
}
562 563

MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
564 565 566
  Handle<JSFunction> function(GetFunction());
  Handle<SharedFunctionInfo> shared(function->shared());
  Handle<ScopeInfo> scope_info(shared->scope_info());
567 568

  Handle<JSObject> local_scope =
569
      isolate_->factory()->NewJSObjectWithNullProto();
570
  MaterializeStackLocals(local_scope, scope_info);
571

572
  Handle<Context> frame_context = GetContext();
573 574 575

  if (!scope_info->HasContext()) return local_scope;

576
  // Fill all context locals.
577
  Handle<Context> function_context(frame_context->closure_context());
578 579 580 581
  CopyContextLocalsToScopeObject(scope_info, function_context, local_scope);

  // Finally copy any properties from the function context extension.
  // These will be variables introduced by eval.
582 583
  if (function_context->closure() == *function &&
      !function_context->IsNativeContext()) {
584
    CopyContextExtensionToScopeObject(function_context, local_scope,
585
                                      KeyCollectionMode::kIncludePrototypes);
586 587 588 589 590 591 592 593 594 595
  }

  return local_scope;
}


// Create a plain JSObject which materializes the closure content for the
// context.
Handle<JSObject> ScopeIterator::MaterializeClosure() {
  Handle<Context> context = CurrentContext();
596
  DCHECK(context->IsFunctionContext() || context->IsEvalContext());
597 598 599 600 601 602 603

  Handle<SharedFunctionInfo> shared(context->closure()->shared());
  Handle<ScopeInfo> scope_info(shared->scope_info());

  // Allocate and initialize a JSObject with all the content of this function
  // closure.
  Handle<JSObject> closure_scope =
604
      isolate_->factory()->NewJSObjectWithNullProto();
605 606 607 608 609 610

  // Fill all context locals to the context extension.
  CopyContextLocalsToScopeObject(scope_info, context, closure_scope);

  // Finally copy any properties from the function context extension. This will
  // be variables introduced by eval.
611 612
  CopyContextExtensionToScopeObject(context, closure_scope,
                                    KeyCollectionMode::kOwnOnly);
613 614 615 616 617 618 619 620 621 622

  return closure_scope;
}


// Create a plain JSObject which materializes the scope for the specified
// catch context.
Handle<JSObject> ScopeIterator::MaterializeCatchScope() {
  Handle<Context> context = CurrentContext();
  DCHECK(context->IsCatchContext());
623
  Handle<String> name(context->catch_name());
624 625 626
  Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
                               isolate_);
  Handle<JSObject> catch_scope =
627
      isolate_->factory()->NewJSObjectWithNullProto();
628 629 630 631 632 633
  JSObject::SetOwnPropertyIgnoreAttributes(catch_scope, name, thrown_object,
                                           NONE)
      .Check();
  return catch_scope;
}

634 635 636 637 638 639 640 641 642 643
// Retrieve the with-context extension object. If the extension object is
// a proxy, return an empty object.
Handle<JSObject> ScopeIterator::WithContextExtension() {
  Handle<Context> context = CurrentContext();
  DCHECK(context->IsWithContext());
  if (context->extension_receiver()->IsJSProxy()) {
    return isolate_->factory()->NewJSObjectWithNullProto();
  }
  return handle(JSObject::cast(context->extension_receiver()));
}
644 645 646

// Create a plain JSObject which materializes the block scope for the specified
// block context.
647 648
Handle<JSObject> ScopeIterator::MaterializeInnerScope() {
  Handle<JSObject> inner_scope =
649
      isolate_->factory()->NewJSObjectWithNullProto();
650 651

  Handle<Context> context = Handle<Context>::null();
652 653
  if (HasNestedScopeChain()) {
    Handle<ScopeInfo> scope_info = LastNestedScopeChain().scope_info;
654
    MaterializeStackLocals(inner_scope, scope_info);
655 656 657 658 659 660 661
    if (scope_info->HasContext()) context = CurrentContext();
  } else {
    context = CurrentContext();
  }

  if (!context.is_null()) {
    // Fill all context locals.
662
    CopyContextLocalsToScopeObject(CurrentScopeInfo(), context, inner_scope);
663 664
    CopyContextExtensionToScopeObject(context, inner_scope,
                                      KeyCollectionMode::kOwnOnly);
665
  }
666
  return inner_scope;
667 668 669 670 671 672 673 674
}


// Create a plain JSObject which materializes the module scope for the specified
// module context.
MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() {
  Handle<Context> context = CurrentContext();
  DCHECK(context->IsModuleContext());
675
  Handle<ScopeInfo> scope_info(context->scope_info());
676
  Handle<JSObject> module_scope =
677
      isolate_->factory()->NewJSObjectWithNullProto();
678
  CopyContextLocalsToScopeObject(scope_info, context, module_scope);
679
  CopyModuleVarsToScopeObject(scope_info, context, module_scope);
680 681 682
  return module_scope;
}

683 684 685 686 687 688 689
bool ScopeIterator::SetParameterValue(Handle<ScopeInfo> scope_info,
                                      Handle<String> parameter_name,
                                      Handle<Object> new_value) {
  // Setting stack locals of optimized frames is not supported.
  HandleScope scope(isolate_);
  for (int i = 0; i < scope_info->ParameterCount(); ++i) {
    if (String::Equals(handle(scope_info->ParameterName(i)), parameter_name)) {
690 691 692 693 694 695 696
      // Suspended generators should not get here because all parameters should
      // be context-allocated.
      DCHECK_NOT_NULL(frame_inspector_);
      JavaScriptFrame* frame = GetFrame();
      if (frame->is_optimized()) {
        return false;
      }
697 698 699 700 701 702 703 704 705 706
      frame->SetParameterValue(i, *new_value);
      return true;
    }
  }
  return false;
}

bool ScopeIterator::SetStackVariableValue(Handle<ScopeInfo> scope_info,
                                          Handle<String> variable_name,
                                          Handle<Object> new_value) {
707 708
  // Setting stack locals of optimized frames is not supported. Suspended
  // generators are supported.
709 710 711
  HandleScope scope(isolate_);
  for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
    if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
712 713 714 715 716 717 718 719 720 721 722 723 724
      int stack_local_index = scope_info->StackLocalIndex(i);
      if (frame_inspector_ != nullptr) {
        // Set the variable on the stack.
        JavaScriptFrame* frame = GetFrame();
        if (frame->is_optimized()) return false;
        frame->SetExpression(stack_local_index, *new_value);
      } else {
        // Set the variable in the suspended generator.
        DCHECK(!generator_.is_null());
        Handle<FixedArray> register_file(generator_->register_file());
        DCHECK_LT(stack_local_index, register_file->length());
        register_file->set(stack_local_index, *new_value);
      }
725 726 727 728 729
      return true;
    }
  }
  return false;
}
730

731 732 733 734 735
bool ScopeIterator::SetContextVariableValue(Handle<ScopeInfo> scope_info,
                                            Handle<Context> context,
                                            Handle<String> variable_name,
                                            Handle<Object> new_value) {
  HandleScope scope(isolate_);
736 737 738 739 740 741
  for (int i = 0; i < scope_info->ContextLocalCount(); i++) {
    Handle<String> next_name(scope_info->ContextLocalName(i));
    if (String::Equals(variable_name, next_name)) {
      VariableMode mode;
      InitializationFlag init_flag;
      MaybeAssignedFlag maybe_assigned_flag;
742 743
      int context_index = ScopeInfo::ContextSlotIndex(
          scope_info, next_name, &mode, &init_flag, &maybe_assigned_flag);
744 745 746 747 748
      context->set(context_index, *new_value);
      return true;
    }
  }

749 750
  // TODO(neis): Clean up context "extension" mess.
  if (!context->IsModuleContext() && context->has_extension()) {
751 752 753 754 755 756 757 758 759 760 761 762
    Handle<JSObject> ext(context->extension_object());
    Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
    DCHECK(maybe.IsJust());
    if (maybe.FromJust()) {
      // We don't expect this to do anything except replacing property value.
      JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value,
                                               NONE)
          .Check();
      return true;
    }
  }

763 764 765 766 767
  return false;
}

bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
                                          Handle<Object> new_value) {
768
  Handle<ScopeInfo> scope_info(GetFunction()->shared()->scope_info());
769

770
  // Parameter might be shadowed in context. Don't stop here.
771
  bool result = SetParameterValue(scope_info, variable_name, new_value);
772 773

  // Stack locals.
774
  if (SetStackVariableValue(scope_info, variable_name, new_value)) {
775
    return true;
776 777
  }

778 779 780 781
  if (scope_info->HasContext() &&
      SetContextVariableValue(scope_info, CurrentContext(), variable_name,
                              new_value)) {
    return true;
782 783
  }

784
  return result;
785 786
}

787 788 789
bool ScopeIterator::SetModuleVariableValue(Handle<String> variable_name,
                                           Handle<Object> new_value) {
  DCHECK_NOT_NULL(frame_inspector_);
790 791

  // Get module context and its scope info.
792
  Handle<Context> context = CurrentContext();
793 794 795 796 797
  while (!context->IsModuleContext()) {
    context = handle(context->previous(), isolate_);
  }
  Handle<ScopeInfo> scope_info(context->scope_info(), isolate_);
  DCHECK_EQ(scope_info->scope_type(), MODULE_SCOPE);
798 799 800 801 802 803 804

  if (SetContextVariableValue(scope_info, context, variable_name, new_value)) {
    return true;
  }

  int cell_index;
  {
805 806 807 808 809
    VariableMode mode;
    InitializationFlag init_flag;
    MaybeAssignedFlag maybe_assigned_flag;
    cell_index = scope_info->ModuleIndex(variable_name, &mode, &init_flag,
                                         &maybe_assigned_flag);
810
  }
811 812 813 814

  // Setting imports is currently not supported.
  bool found = ModuleDescriptor::GetCellIndexKind(cell_index) ==
               ModuleDescriptor::kExport;
815 816 817 818
  if (found) {
    Module::StoreVariable(handle(context->module(), isolate_), cell_index,
                          new_value);
  }
819
  return found;
820 821
}

822 823
bool ScopeIterator::SetInnerScopeVariableValue(Handle<String> variable_name,
                                               Handle<Object> new_value) {
824
  Handle<ScopeInfo> scope_info = CurrentScopeInfo();
825 826
  DCHECK(scope_info->scope_type() == BLOCK_SCOPE ||
         scope_info->scope_type() == EVAL_SCOPE);
827

828
  // Setting stack locals of optimized frames is not supported.
829
  if (SetStackVariableValue(scope_info, variable_name, new_value)) {
830
    return true;
831 832
  }

833 834 835
  if (HasContext() && SetContextVariableValue(scope_info, CurrentContext(),
                                              variable_name, new_value)) {
    return true;
836
  }
837

838 839 840 841 842 843
  return false;
}

// This method copies structure of MaterializeClosure method above.
bool ScopeIterator::SetClosureVariableValue(Handle<String> variable_name,
                                            Handle<Object> new_value) {
844 845
  DCHECK(CurrentContext()->IsFunctionContext() ||
         CurrentContext()->IsEvalContext());
846 847
  return SetContextVariableValue(CurrentScopeInfo(), CurrentContext(),
                                 variable_name, new_value);
848 849 850 851
}

bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
                                           Handle<Object> new_value) {
852 853
  Handle<String> internalized_variable_name =
      isolate_->factory()->InternalizeString(variable_name);
854 855 856 857
  Handle<Context> context = CurrentContext();
  Handle<ScriptContextTable> script_contexts(
      context->global_object()->native_context()->script_context_table());
  ScriptContextTable::LookupResult lookup_result;
858
  if (ScriptContextTable::Lookup(script_contexts, internalized_variable_name,
859 860 861 862 863 864 865 866 867 868 869 870 871 872
                                 &lookup_result)) {
    Handle<Context> script_context = ScriptContextTable::GetContext(
        script_contexts, lookup_result.context_index);
    script_context->set(lookup_result.slot_index, *new_value);
    return true;
  }

  return false;
}

bool ScopeIterator::SetCatchVariableValue(Handle<String> variable_name,
                                          Handle<Object> new_value) {
  Handle<Context> context = CurrentContext();
  DCHECK(context->IsCatchContext());
873
  Handle<String> name(context->catch_name());
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
  if (!String::Equals(name, variable_name)) {
    return false;
  }
  context->set(Context::THROWN_OBJECT_INDEX, *new_value);
  return true;
}


void ScopeIterator::CopyContextLocalsToScopeObject(
    Handle<ScopeInfo> scope_info, Handle<Context> context,
    Handle<JSObject> scope_object) {
  Isolate* isolate = scope_info->GetIsolate();
  int local_count = scope_info->ContextLocalCount();
  if (local_count == 0) return;
  // Fill all context locals to the context extension.
  for (int i = 0; i < local_count; ++i) {
890 891
    Handle<String> name(scope_info->ContextLocalName(i));
    if (ScopeInfo::VariableIsSynthetic(*name)) continue;
892 893 894
    int context_index = Context::MIN_CONTEXT_SLOTS + i;
    Handle<Object> value = Handle<Object>(context->get(context_index), isolate);
    // Reflect variables under TDZ as undefined in scope object.
895
    if (value->IsTheHole(isolate)) continue;
896 897
    // This should always succeed.
    // TODO(verwaest): Use AddDataProperty instead.
898
    JSObject::SetOwnPropertyIgnoreAttributes(scope_object, name, value, NONE)
899
        .Check();
900 901 902 903 904 905 906 907 908 909 910 911 912
  }
}

void ScopeIterator::CopyModuleVarsToScopeObject(Handle<ScopeInfo> scope_info,
                                                Handle<Context> context,
                                                Handle<JSObject> scope_object) {
  Isolate* isolate = scope_info->GetIsolate();

  int module_variable_count =
      Smi::cast(scope_info->get(scope_info->ModuleVariableCountIndex()))
          ->value();
  for (int i = 0; i < module_variable_count; ++i) {
    Handle<String> local_name;
913
    Handle<Object> value;
914 915 916 917 918 919
    {
      String* name;
      int index;
      scope_info->ModuleVariable(i, &name, &index);
      CHECK(!ScopeInfo::VariableIsSynthetic(name));
      local_name = handle(name, isolate);
920
      value = Module::LoadVariable(handle(context->module(), isolate), index);
921 922 923 924 925 926 927 928 929
    }

    // Reflect variables under TDZ as undefined in scope object.
    if (value->IsTheHole(isolate)) continue;
    // This should always succeed.
    // TODO(verwaest): Use AddDataProperty instead.
    JSObject::SetOwnPropertyIgnoreAttributes(scope_object, local_name, value,
                                             NONE)
        .Check();
930 931 932
  }
}

933 934
void ScopeIterator::CopyContextExtensionToScopeObject(
    Handle<Context> context, Handle<JSObject> scope_object,
935
    KeyCollectionMode mode) {
936 937 938
  if (context->extension_object() == nullptr) return;
  Handle<JSObject> extension(context->extension_object());
  Handle<FixedArray> keys =
939
      KeyAccumulator::GetKeys(extension, mode, ENUMERABLE_STRINGS)
940
          .ToHandleChecked();
941 942 943 944 945

  for (int i = 0; i < keys->length(); i++) {
    // Names of variables introduced by eval are strings.
    DCHECK(keys->get(i)->IsString());
    Handle<String> key(String::cast(keys->get(i)));
946 947 948 949
    Handle<Object> value =
        Object::GetPropertyOrElement(extension, key).ToHandleChecked();
    JSObject::SetOwnPropertyIgnoreAttributes(scope_object, key, value, NONE)
        .Check();
950 951 952
  }
}

953 954
void ScopeIterator::GetNestedScopeChain(Isolate* isolate, Scope* scope,
                                        int position) {
955 956
  if (scope->is_function_scope()) {
    // Do not collect scopes of nested inner functions inside the current one.
957
    // Nested arrow functions could have the same end positions.
958
    Handle<JSFunction> function = GetFunction();
959 960 961 962
    if (scope->start_position() > function->shared()->start_position() &&
        scope->end_position() <= function->shared()->end_position()) {
      return;
    }
963
  }
964 965 966
  if (scope->is_hidden()) {
    // We need to add this chain element in case the scope has a context
    // associated. We need to keep the scope chain and context chain in sync.
967
    nested_scope_chain_.emplace_back(scope->scope_info());
968
  } else {
969 970
    nested_scope_chain_.emplace_back(
        scope->scope_info(), scope->start_position(), scope->end_position());
971
  }
972 973
  for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr;
       inner_scope = inner_scope->sibling()) {
974 975
    int beg_pos = inner_scope->start_position();
    int end_pos = inner_scope->end_position();
976
    DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden());
977 978 979 980 981 982 983
    if (beg_pos <= position && position < end_pos) {
      GetNestedScopeChain(isolate, inner_scope, position);
      return;
    }
  }
}

984
bool ScopeIterator::HasNestedScopeChain() {
985
  return !nested_scope_chain_.empty();
986 987 988 989
}

ScopeIterator::ExtendedScopeInfo& ScopeIterator::LastNestedScopeChain() {
  DCHECK(HasNestedScopeChain());
990
  return nested_scope_chain_.back();
991 992
}

993 994
}  // namespace internal
}  // namespace v8