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