Line data Source code
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 <memory>
8 :
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"
17 : #include "src/parsing/rewriter.h"
18 :
19 : namespace v8 {
20 : namespace internal {
21 :
22 135623 : ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
23 : ScopeIterator::Option option)
24 : : isolate_(isolate),
25 : frame_inspector_(frame_inspector),
26 271246 : seen_script_scope_(false) {
27 271246 : if (!frame_inspector->GetContext()->IsContext()) {
28 : // Optimized frame, context or function cannot be materialized. Give up.
29 135623 : return;
30 : }
31 :
32 : // We should not instantiate a ScopeIterator for wasm frames.
33 : DCHECK(frame_inspector->GetScript()->type() != Script::TYPE_WASM);
34 :
35 135509 : TryParseAndRetrieveScopes(option);
36 : }
37 :
38 211693 : void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
39 136894 : context_ = GetContext();
40 :
41 : // Catch the case when the debugger stops in an internal function.
42 136894 : Handle<JSFunction> function = GetFunction();
43 : Handle<SharedFunctionInfo> shared_info(function->shared());
44 : Handle<ScopeInfo> scope_info(shared_info->scope_info());
45 273788 : if (shared_info->script()->IsUndefined(isolate_)) {
46 0 : while (context_->closure() == *function) {
47 0 : context_ = Handle<Context>(context_->previous(), isolate_);
48 : }
49 5499 : return;
50 : }
51 :
52 : // Currently it takes too much time to find nested scopes due to script
53 : // parsing. Sometimes we want to run the ScopeIterator as fast as possible
54 : // (for example, while collecting async call stacks on every
55 : // addEventListener call), even if we drop some nested scopes.
56 : // Later we may optimize getting the nested scopes (cache the result?)
57 : // and include nested scopes into the "fast" iteration case as well.
58 136894 : bool ignore_nested_scopes = (option == IGNORE_NESTED_SCOPES);
59 136894 : bool collect_non_locals = (option == COLLECT_NON_LOCALS);
60 348587 : if (!ignore_nested_scopes && shared_info->HasBreakInfo() &&
61 74799 : frame_inspector_ != nullptr) {
62 : // The source position at return is always the end of the function,
63 : // which is not consistent with the current scope chain. Therefore all
64 : // nested with, catch and block contexts are skipped, and we can only
65 : // inspect the function scope.
66 : // This can only happen if we set a break point inside right before the
67 : // return, which requires a debug info to be available.
68 74799 : Handle<DebugInfo> debug_info(shared_info->GetDebugInfo());
69 :
70 : // Find the break point where execution has stopped.
71 74799 : BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame());
72 :
73 74799 : ignore_nested_scopes = location.IsReturn();
74 : }
75 :
76 136894 : if (ignore_nested_scopes) {
77 5940 : if (scope_info->HasContext()) {
78 261 : context_ = Handle<Context>(context_->declaration_context(), isolate_);
79 : } else {
80 5935 : while (context_->closure() == *function) {
81 164 : context_ = Handle<Context>(context_->previous(), isolate_);
82 : }
83 : }
84 5940 : if (scope_info->scope_type() == FUNCTION_SCOPE) {
85 : nested_scope_chain_.emplace_back(scope_info,
86 : shared_info->start_position(),
87 7552 : shared_info->end_position());
88 : }
89 5940 : if (!collect_non_locals) return;
90 : }
91 :
92 : // Reparse the code and analyze the scopes.
93 : // Check whether we are in global, eval or function code.
94 : std::unique_ptr<ParseInfo> info;
95 131395 : if (scope_info->scope_type() != FUNCTION_SCOPE) {
96 : // Global or eval code.
97 : Handle<Script> script(Script::cast(shared_info->script()));
98 51667 : info.reset(new ParseInfo(script));
99 51667 : if (scope_info->scope_type() == EVAL_SCOPE) {
100 : info->set_eval();
101 3821 : if (!function->context()->IsNativeContext()) {
102 1614 : info->set_outer_scope_info(handle(function->context()->scope_info()));
103 : }
104 : // Language mode may be inherited from the eval caller.
105 : // Retrieve it from shared function info.
106 : info->set_language_mode(shared_info->language_mode());
107 47846 : } else if (scope_info->scope_type() == MODULE_SCOPE) {
108 : info->set_module();
109 : } else {
110 : DCHECK(scope_info->scope_type() == SCRIPT_SCOPE);
111 : }
112 : } else {
113 : // Inner function.
114 79728 : info.reset(new ParseInfo(shared_info));
115 : }
116 262298 : if (parsing::ParseAny(info.get(), shared_info, isolate_) &&
117 130903 : Rewriter::Rewrite(info.get())) {
118 261806 : info->ast_value_factory()->Internalize(isolate_);
119 130903 : DeclarationScope* scope = info->literal()->scope();
120 130903 : if (!ignore_nested_scopes || collect_non_locals) {
121 130903 : CollectNonLocals(info.get(), scope);
122 : }
123 130903 : if (!ignore_nested_scopes) {
124 130462 : DeclarationScope::Analyze(info.get());
125 : DeclarationScope::AllocateScopeInfos(info.get(), isolate_,
126 130462 : AnalyzeMode::kDebugger);
127 130462 : RetrieveScopeChain(scope);
128 : }
129 : } else {
130 : // A failed reparse indicates that the preparser has diverged from the
131 : // parser or that the preparse data given to the initial parse has been
132 : // faulty. We fail in debug mode but in release mode we only provide the
133 : // information we get from the context chain but nothing about
134 : // completely stack allocated scopes or stack allocated locals.
135 : // Or it could be due to stack overflow.
136 : // Silently fail by presenting an empty context chain.
137 492 : CHECK(isolate_->has_pending_exception());
138 492 : isolate_->clear_pending_exception();
139 492 : context_ = Handle<Context>();
140 : }
141 131395 : UnwrapEvaluationContext();
142 : }
143 :
144 25 : ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
145 : : isolate_(isolate),
146 : context_(function->context()),
147 50 : seen_script_scope_(false) {
148 25 : if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>();
149 25 : UnwrapEvaluationContext();
150 25 : }
151 :
152 1385 : ScopeIterator::ScopeIterator(Isolate* isolate,
153 : Handle<JSGeneratorObject> generator)
154 : : isolate_(isolate),
155 : generator_(generator),
156 : context_(generator->context()),
157 2770 : seen_script_scope_(false) {
158 1385 : if (!generator->function()->shared()->IsSubjectToDebugging()) {
159 0 : context_ = Handle<Context>();
160 1385 : return;
161 : }
162 1385 : TryParseAndRetrieveScopes(DEFAULT);
163 : }
164 :
165 474872 : void ScopeIterator::UnwrapEvaluationContext() {
166 : while (true) {
167 474872 : if (context_.is_null()) return;
168 349801 : if (!context_->IsDebugEvaluateContext()) return;
169 : Handle<Object> wrapped(context_->get(Context::WRAPPED_CONTEXT_INDEX),
170 0 : isolate_);
171 0 : if (wrapped->IsContext()) {
172 0 : context_ = Handle<Context>::cast(wrapped);
173 : } else {
174 0 : context_ = Handle<Context>(context_->previous(), isolate_);
175 : }
176 : }
177 : }
178 :
179 :
180 965 : MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() {
181 : // Calculate the size of the result.
182 : Handle<FixedArray> details =
183 965 : isolate_->factory()->NewFixedArray(kScopeDetailsSize);
184 : // Fill in scope details.
185 965 : details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type()));
186 : Handle<JSObject> scope_object;
187 1930 : ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject);
188 965 : details->set(kScopeDetailsObjectIndex, *scope_object);
189 965 : if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
190 670 : return isolate_->factory()->NewJSArrayWithElements(details);
191 : }
192 :
193 630 : Handle<JSFunction> js_function = GetClosure();
194 630 : if (!js_function.is_null()) {
195 630 : Handle<String> closure_name = JSFunction::GetDebugName(js_function);
196 1260 : if (!closure_name.is_null() && closure_name->length() != 0) {
197 612 : details->set(kScopeDetailsNameIndex, *closure_name);
198 : }
199 : details->set(kScopeDetailsStartPositionIndex,
200 630 : Smi::FromInt(start_position()));
201 630 : details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position()));
202 630 : details->set(kScopeDetailsFunctionIndex, *js_function);
203 : }
204 1260 : return isolate_->factory()->NewJSArrayWithElements(details);
205 : }
206 :
207 503032 : Handle<JSFunction> ScopeIterator::GetClosure() {
208 503032 : if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript)
209 : return Handle<JSFunction>::null();
210 265319 : if (HasNestedScopeChain()) return GetFunction();
211 142668 : return HasContext() ? handle(CurrentContext()->closure())
212 35667 : : Handle<JSFunction>::null();
213 : }
214 :
215 88843 : int ScopeIterator::start_position() {
216 88843 : if (HasNestedScopeChain()) {
217 76947 : return LastNestedScopeChain().start_position;
218 : }
219 11896 : if (!HasContext()) return 0;
220 23792 : Handle<JSFunction> js_function = handle(CurrentContext()->closure());
221 23792 : return js_function.is_null() ? 0 : js_function->shared()->start_position();
222 : }
223 :
224 88843 : int ScopeIterator::end_position() {
225 88843 : if (HasNestedScopeChain()) {
226 76947 : return LastNestedScopeChain().end_position;
227 : }
228 11896 : if (!HasContext()) return 0;
229 23792 : Handle<JSFunction> js_function = handle(CurrentContext()->closure());
230 23792 : return js_function.is_null() ? 0 : js_function->shared()->end_position();
231 : }
232 :
233 343452 : void ScopeIterator::Next() {
234 : DCHECK(!Done());
235 343452 : ScopeType scope_type = Type();
236 343452 : if (scope_type == ScopeTypeGlobal) {
237 : // The global scope is always the last in the chain.
238 : DCHECK(context_->IsNativeContext());
239 124579 : context_ = Handle<Context>();
240 218873 : } else if (scope_type == ScopeTypeScript) {
241 124741 : seen_script_scope_ = true;
242 124741 : if (context_->IsScriptContext()) {
243 16456 : context_ = Handle<Context>(context_->previous(), isolate_);
244 : }
245 124741 : if (HasNestedScopeChain()) {
246 : DCHECK_EQ(LastNestedScopeChain().scope_info->scope_type(), SCRIPT_SCOPE);
247 : nested_scope_chain_.pop_back();
248 : DCHECK(!HasNestedScopeChain());
249 : }
250 124741 : CHECK(context_->IsNativeContext());
251 94132 : } else if (!HasNestedScopeChain()) {
252 23928 : context_ = Handle<Context>(context_->previous(), isolate_);
253 : } else {
254 7376 : do {
255 83372 : if (LastNestedScopeChain().scope_info->HasContext()) {
256 : DCHECK(context_->previous() != nullptr);
257 29202 : context_ = Handle<Context>(context_->previous(), isolate_);
258 : }
259 : nested_scope_chain_.pop_back();
260 83372 : if (!HasNestedScopeChain()) break;
261 : // Repeat to skip hidden scopes.
262 : } while (LastNestedScopeChain().is_hidden());
263 : }
264 343452 : UnwrapEvaluationContext();
265 343452 : }
266 :
267 :
268 : // Return the type of the current scope.
269 3201388 : ScopeIterator::ScopeType ScopeIterator::Type() {
270 : DCHECK(!Done());
271 3201388 : if (HasNestedScopeChain()) {
272 1321040 : Handle<ScopeInfo> scope_info = LastNestedScopeChain().scope_info;
273 1321040 : switch (scope_info->scope_type()) {
274 : case FUNCTION_SCOPE:
275 : DCHECK(context_->IsFunctionContext() || !scope_info->HasContext());
276 : return ScopeTypeLocal;
277 : case MODULE_SCOPE:
278 : DCHECK(context_->IsModuleContext());
279 7660 : return ScopeTypeModule;
280 : case SCRIPT_SCOPE:
281 : DCHECK(context_->IsScriptContext() || context_->IsNativeContext());
282 494305 : return ScopeTypeScript;
283 : case WITH_SCOPE:
284 : DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
285 25041 : return ScopeTypeWith;
286 : case CATCH_SCOPE:
287 : DCHECK(context_->IsCatchContext());
288 9814 : return ScopeTypeCatch;
289 : case BLOCK_SCOPE:
290 : DCHECK(!scope_info->HasContext() || context_->IsBlockContext());
291 27756 : return ScopeTypeBlock;
292 : case EVAL_SCOPE:
293 : DCHECK(!scope_info->HasContext() || context_->IsEvalContext());
294 12369 : return ScopeTypeEval;
295 : }
296 0 : UNREACHABLE();
297 : }
298 1880348 : if (context_->IsNativeContext()) {
299 : DCHECK(context_->global_object()->IsJSGlobalObject());
300 : // If we are at the native context and have not yet seen script scope,
301 : // fake it.
302 1483964 : return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript;
303 : }
304 512583 : if (context_->IsFunctionContext() || context_->IsEvalContext()) {
305 : return ScopeTypeClosure;
306 : }
307 115909 : if (context_->IsCatchContext()) {
308 : return ScopeTypeCatch;
309 : }
310 107424 : if (context_->IsBlockContext()) {
311 : return ScopeTypeBlock;
312 : }
313 106089 : if (context_->IsModuleContext()) {
314 : return ScopeTypeModule;
315 : }
316 83544 : if (context_->IsScriptContext()) {
317 : return ScopeTypeScript;
318 : }
319 : DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext());
320 11233 : return ScopeTypeWith;
321 : }
322 :
323 :
324 458741 : MaybeHandle<JSObject> ScopeIterator::ScopeObject() {
325 : DCHECK(!Done());
326 458741 : switch (Type()) {
327 : case ScopeIterator::ScopeTypeGlobal:
328 373332 : return Handle<JSObject>(CurrentContext()->global_proxy());
329 : case ScopeIterator::ScopeTypeScript:
330 237896 : return MaterializeScriptScope();
331 : case ScopeIterator::ScopeTypeLocal:
332 : // Materialize the content of the local scope into a JSObject.
333 : DCHECK_EQ(1, nested_scope_chain_.size());
334 71247 : return MaterializeLocalScope();
335 : case ScopeIterator::ScopeTypeWith:
336 2463 : return WithContextExtension();
337 : case ScopeIterator::ScopeTypeCatch:
338 1106 : return MaterializeCatchScope();
339 : case ScopeIterator::ScopeTypeClosure:
340 : // Materialize the content of the closure scope into a JSObject.
341 10387 : return MaterializeClosure();
342 : case ScopeIterator::ScopeTypeBlock:
343 : case ScopeIterator::ScopeTypeEval:
344 8763 : return MaterializeInnerScope();
345 : case ScopeIterator::ScopeTypeModule:
346 2435 : return MaterializeModuleScope();
347 : }
348 0 : UNREACHABLE();
349 : }
350 :
351 :
352 80064 : bool ScopeIterator::HasContext() {
353 80064 : ScopeType type = Type();
354 80064 : if (type == ScopeTypeBlock || type == ScopeTypeLocal ||
355 : type == ScopeTypeEval) {
356 20815 : if (HasNestedScopeChain()) {
357 20605 : return LastNestedScopeChain().scope_info->HasContext();
358 : }
359 : }
360 : return true;
361 : }
362 :
363 :
364 353 : bool ScopeIterator::SetVariableValue(Handle<String> variable_name,
365 : Handle<Object> new_value) {
366 : DCHECK(!Done());
367 353 : switch (Type()) {
368 : case ScopeIterator::ScopeTypeGlobal:
369 : break;
370 : case ScopeIterator::ScopeTypeLocal:
371 145 : return SetLocalVariableValue(variable_name, new_value);
372 : case ScopeIterator::ScopeTypeWith:
373 : break;
374 : case ScopeIterator::ScopeTypeCatch:
375 19 : return SetCatchVariableValue(variable_name, new_value);
376 : case ScopeIterator::ScopeTypeClosure:
377 50 : return SetClosureVariableValue(variable_name, new_value);
378 : case ScopeIterator::ScopeTypeScript:
379 34 : return SetScriptVariableValue(variable_name, new_value);
380 : case ScopeIterator::ScopeTypeBlock:
381 : case ScopeIterator::ScopeTypeEval:
382 34 : return SetInnerScopeVariableValue(variable_name, new_value);
383 : case ScopeIterator::ScopeTypeModule:
384 35 : return SetModuleVariableValue(variable_name, new_value);
385 : break;
386 : }
387 : return false;
388 : }
389 :
390 :
391 11187 : Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
392 : DCHECK(!Done());
393 11187 : if (HasNestedScopeChain()) {
394 11038 : return LastNestedScopeChain().scope_info;
395 204 : } else if (context_->IsBlockContext() || context_->IsFunctionContext() ||
396 5 : context_->IsEvalContext()) {
397 298 : return Handle<ScopeInfo>(context_->scope_info());
398 : }
399 : return Handle<ScopeInfo>::null();
400 : }
401 :
402 :
403 440306 : Handle<Context> ScopeIterator::CurrentContext() {
404 : DCHECK(!Done());
405 518238 : if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript ||
406 : !HasNestedScopeChain()) {
407 434639 : return context_;
408 5667 : } else if (LastNestedScopeChain().scope_info->HasContext()) {
409 5667 : return context_;
410 : } else {
411 : return Handle<Context>::null();
412 : }
413 : }
414 :
415 10148 : Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; }
416 :
417 : #ifdef DEBUG
418 : // Debug print of the content of the current scope.
419 : void ScopeIterator::DebugPrint() {
420 : OFStream os(stdout);
421 : DCHECK(!Done());
422 : switch (Type()) {
423 : case ScopeIterator::ScopeTypeGlobal:
424 : os << "Global:\n";
425 : CurrentContext()->Print(os);
426 : break;
427 :
428 : case ScopeIterator::ScopeTypeLocal: {
429 : os << "Local:\n";
430 : GetFunction()->shared()->scope_info()->Print();
431 : if (!CurrentContext().is_null()) {
432 : CurrentContext()->Print(os);
433 : if (CurrentContext()->has_extension()) {
434 : Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
435 : if (extension->IsJSContextExtensionObject()) {
436 : extension->Print(os);
437 : }
438 : }
439 : }
440 : break;
441 : }
442 :
443 : case ScopeIterator::ScopeTypeWith:
444 : os << "With:\n";
445 : CurrentContext()->extension()->Print(os);
446 : break;
447 :
448 : case ScopeIterator::ScopeTypeCatch:
449 : os << "Catch:\n";
450 : CurrentContext()->extension()->Print(os);
451 : CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os);
452 : break;
453 :
454 : case ScopeIterator::ScopeTypeClosure:
455 : os << "Closure:\n";
456 : CurrentContext()->Print(os);
457 : if (CurrentContext()->has_extension()) {
458 : Handle<HeapObject> extension(CurrentContext()->extension(), isolate_);
459 : if (extension->IsJSContextExtensionObject()) {
460 : extension->Print(os);
461 : }
462 : }
463 : break;
464 :
465 : case ScopeIterator::ScopeTypeScript:
466 : os << "Script:\n";
467 : CurrentContext()
468 : ->global_object()
469 : ->native_context()
470 : ->script_context_table()
471 : ->Print(os);
472 : break;
473 :
474 : default:
475 : UNREACHABLE();
476 : }
477 : PrintF("\n");
478 : }
479 : #endif
480 :
481 208141 : inline Handle<Context> ScopeIterator::GetContext() {
482 208141 : if (frame_inspector_) {
483 206379 : return Handle<Context>::cast(frame_inspector_->GetContext());
484 : } else {
485 : DCHECK(!generator_.is_null());
486 : return handle(generator_->context());
487 : }
488 : }
489 :
490 516764 : Handle<JSFunction> ScopeIterator::GetFunction() {
491 516764 : if (frame_inspector_) {
492 : return frame_inspector_->GetFunction();
493 : } else {
494 : DCHECK(!generator_.is_null());
495 : return handle(generator_->function());
496 : }
497 : }
498 :
499 0 : int ScopeIterator::GetSourcePosition() {
500 130462 : if (frame_inspector_) {
501 0 : return frame_inspector_->GetSourcePosition();
502 : } else {
503 : DCHECK(!generator_.is_null());
504 1385 : return generator_->source_position();
505 : }
506 : }
507 :
508 130462 : void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) {
509 : DCHECK_NOT_NULL(scope);
510 130462 : GetNestedScopeChain(isolate_, scope, GetSourcePosition());
511 130462 : }
512 :
513 130903 : void ScopeIterator::CollectNonLocals(ParseInfo* info, DeclarationScope* scope) {
514 : DCHECK_NOT_NULL(scope);
515 : DCHECK(non_locals_.is_null());
516 130903 : non_locals_ = scope->CollectNonLocals(info, StringSet::New(isolate_));
517 130903 : }
518 :
519 :
520 237896 : MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() {
521 713688 : Handle<JSGlobalObject> global(CurrentContext()->global_object());
522 : Handle<ScriptContextTable> script_contexts(
523 : global->native_context()->script_context_table());
524 :
525 : Handle<JSObject> script_scope =
526 237896 : isolate_->factory()->NewJSObjectWithNullProto();
527 :
528 1440388 : for (int context_index = 0; context_index < script_contexts->used();
529 : context_index++) {
530 : Handle<Context> context =
531 482298 : ScriptContextTable::GetContext(script_contexts, context_index);
532 482298 : Handle<ScopeInfo> scope_info(context->scope_info());
533 482298 : CopyContextLocalsToScopeObject(scope_info, context, script_scope);
534 : }
535 237896 : return script_scope;
536 : }
537 :
538 79911 : void ScopeIterator::MaterializeStackLocals(Handle<JSObject> local_scope,
539 : Handle<ScopeInfo> scope_info) {
540 79911 : if (frame_inspector_) {
541 159382 : return frame_inspector_->MaterializeStackLocals(local_scope, scope_info);
542 : }
543 :
544 : DCHECK(!generator_.is_null());
545 : // Fill all stack locals.
546 : Handle<FixedArray> register_file(generator_->register_file());
547 2338 : for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
548 729 : Handle<String> name = handle(scope_info->StackLocalName(i));
549 729 : if (ScopeInfo::VariableIsSynthetic(*name)) continue;
550 : Handle<Object> value(register_file->get(scope_info->StackLocalIndex(i)),
551 704 : isolate_);
552 : // TODO(yangguo): We convert optimized out values to {undefined} when they
553 : // are passed to the debugger. Eventually we should handle them somehow.
554 1056 : if (value->IsTheHole(isolate_) || value->IsOptimizedOut(isolate_)) {
555 : DCHECK(!value.is_identical_to(isolate_->factory()->stale_register()));
556 0 : value = isolate_->factory()->undefined_value();
557 : }
558 352 : JSObject::SetOwnPropertyIgnoreAttributes(local_scope, name, value, NONE)
559 704 : .Check();
560 : }
561 : }
562 :
563 71247 : MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
564 71247 : Handle<JSFunction> function(GetFunction());
565 : Handle<SharedFunctionInfo> shared(function->shared());
566 : Handle<ScopeInfo> scope_info(shared->scope_info());
567 :
568 : Handle<JSObject> local_scope =
569 71247 : isolate_->factory()->NewJSObjectWithNullProto();
570 71247 : MaterializeStackLocals(local_scope, scope_info);
571 :
572 71247 : Handle<Context> frame_context = GetContext();
573 :
574 71247 : if (!scope_info->HasContext()) return local_scope;
575 :
576 : // Fill all context locals.
577 10270 : Handle<Context> function_context(frame_context->closure_context());
578 10270 : CopyContextLocalsToScopeObject(scope_info, function_context, local_scope);
579 :
580 : // Finally copy any properties from the function context extension.
581 : // These will be variables introduced by eval.
582 20540 : if (function_context->closure() == *function &&
583 10270 : !function_context->IsNativeContext()) {
584 : CopyContextExtensionToScopeObject(function_context, local_scope,
585 10270 : KeyCollectionMode::kIncludePrototypes);
586 : }
587 :
588 10270 : return local_scope;
589 : }
590 :
591 :
592 : // Create a plain JSObject which materializes the closure content for the
593 : // context.
594 10387 : Handle<JSObject> ScopeIterator::MaterializeClosure() {
595 10387 : Handle<Context> context = CurrentContext();
596 : DCHECK(context->IsFunctionContext() || context->IsEvalContext());
597 :
598 : Handle<SharedFunctionInfo> shared(context->closure()->shared());
599 : Handle<ScopeInfo> scope_info(shared->scope_info());
600 :
601 : // Allocate and initialize a JSObject with all the content of this function
602 : // closure.
603 : Handle<JSObject> closure_scope =
604 10387 : isolate_->factory()->NewJSObjectWithNullProto();
605 :
606 : // Fill all context locals to the context extension.
607 10387 : CopyContextLocalsToScopeObject(scope_info, context, closure_scope);
608 :
609 : // Finally copy any properties from the function context extension. This will
610 : // be variables introduced by eval.
611 : CopyContextExtensionToScopeObject(context, closure_scope,
612 10387 : KeyCollectionMode::kOwnOnly);
613 :
614 10387 : return closure_scope;
615 : }
616 :
617 :
618 : // Create a plain JSObject which materializes the scope for the specified
619 : // catch context.
620 1106 : Handle<JSObject> ScopeIterator::MaterializeCatchScope() {
621 1106 : Handle<Context> context = CurrentContext();
622 : DCHECK(context->IsCatchContext());
623 1106 : Handle<String> name(context->catch_name());
624 : Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
625 1106 : isolate_);
626 : Handle<JSObject> catch_scope =
627 1106 : isolate_->factory()->NewJSObjectWithNullProto();
628 : JSObject::SetOwnPropertyIgnoreAttributes(catch_scope, name, thrown_object,
629 1106 : NONE)
630 2212 : .Check();
631 1106 : return catch_scope;
632 : }
633 :
634 : // Retrieve the with-context extension object. If the extension object is
635 : // a proxy, return an empty object.
636 2463 : Handle<JSObject> ScopeIterator::WithContextExtension() {
637 2463 : Handle<Context> context = CurrentContext();
638 : DCHECK(context->IsWithContext());
639 4926 : if (context->extension_receiver()->IsJSProxy()) {
640 0 : return isolate_->factory()->NewJSObjectWithNullProto();
641 : }
642 2463 : return handle(JSObject::cast(context->extension_receiver()));
643 : }
644 :
645 : // Create a plain JSObject which materializes the block scope for the specified
646 : // block context.
647 8763 : Handle<JSObject> ScopeIterator::MaterializeInnerScope() {
648 : Handle<JSObject> inner_scope =
649 8763 : isolate_->factory()->NewJSObjectWithNullProto();
650 :
651 : Handle<Context> context = Handle<Context>::null();
652 8763 : if (HasNestedScopeChain()) {
653 8664 : Handle<ScopeInfo> scope_info = LastNestedScopeChain().scope_info;
654 8664 : MaterializeStackLocals(inner_scope, scope_info);
655 8664 : if (scope_info->HasContext()) context = CurrentContext();
656 : } else {
657 99 : context = CurrentContext();
658 : }
659 :
660 8763 : if (!context.is_null()) {
661 : // Fill all context locals.
662 367 : CopyContextLocalsToScopeObject(CurrentScopeInfo(), context, inner_scope);
663 : CopyContextExtensionToScopeObject(context, inner_scope,
664 367 : KeyCollectionMode::kOwnOnly);
665 : }
666 8763 : return inner_scope;
667 : }
668 :
669 :
670 : // Create a plain JSObject which materializes the module scope for the specified
671 : // module context.
672 2435 : MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() {
673 2435 : Handle<Context> context = CurrentContext();
674 : DCHECK(context->IsModuleContext());
675 2435 : Handle<ScopeInfo> scope_info(context->scope_info());
676 : Handle<JSObject> module_scope =
677 2435 : isolate_->factory()->NewJSObjectWithNullProto();
678 2435 : CopyContextLocalsToScopeObject(scope_info, context, module_scope);
679 2435 : CopyModuleVarsToScopeObject(scope_info, context, module_scope);
680 2435 : return module_scope;
681 : }
682 :
683 145 : bool ScopeIterator::SetParameterValue(Handle<ScopeInfo> scope_info,
684 : Handle<String> parameter_name,
685 20 : Handle<Object> new_value) {
686 : // Setting stack locals of optimized frames is not supported.
687 145 : HandleScope scope(isolate_);
688 290 : for (int i = 0; i < scope_info->ParameterCount(); ++i) {
689 40 : if (String::Equals(handle(scope_info->ParameterName(i)), parameter_name)) {
690 : // Suspended generators should not get here because all parameters should
691 : // be context-allocated.
692 : DCHECK_NOT_NULL(frame_inspector_);
693 : JavaScriptFrame* frame = GetFrame();
694 40 : if (frame->is_optimized()) {
695 : return false;
696 : }
697 20 : frame->SetParameterValue(i, *new_value);
698 20 : return true;
699 : }
700 : }
701 : return false;
702 : }
703 :
704 179 : bool ScopeIterator::SetStackVariableValue(Handle<ScopeInfo> scope_info,
705 : Handle<String> variable_name,
706 : Handle<Object> new_value) {
707 : // Setting stack locals of optimized frames is not supported. Suspended
708 : // generators are supported.
709 179 : HandleScope scope(isolate_);
710 600 : for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
711 408 : if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
712 83 : int stack_local_index = scope_info->StackLocalIndex(i);
713 83 : if (frame_inspector_ != nullptr) {
714 : // Set the variable on the stack.
715 : JavaScriptFrame* frame = GetFrame();
716 112 : if (frame->is_optimized()) return false;
717 52 : frame->SetExpression(stack_local_index, *new_value);
718 : } else {
719 : // Set the variable in the suspended generator.
720 : DCHECK(!generator_.is_null());
721 : Handle<FixedArray> register_file(generator_->register_file());
722 : DCHECK_LT(stack_local_index, register_file->length());
723 27 : register_file->set(stack_local_index, *new_value);
724 : }
725 : return true;
726 : }
727 : }
728 : return false;
729 : }
730 :
731 162 : bool ScopeIterator::SetContextVariableValue(Handle<ScopeInfo> scope_info,
732 : Handle<Context> context,
733 : Handle<String> variable_name,
734 : Handle<Object> new_value) {
735 162 : HandleScope scope(isolate_);
736 1316 : for (int i = 0; i < scope_info->ContextLocalCount(); i++) {
737 614 : Handle<String> next_name(scope_info->ContextLocalName(i));
738 614 : if (String::Equals(variable_name, next_name)) {
739 : VariableMode mode;
740 : InitializationFlag init_flag;
741 : MaybeAssignedFlag maybe_assigned_flag;
742 : int context_index = ScopeInfo::ContextSlotIndex(
743 118 : scope_info, next_name, &mode, &init_flag, &maybe_assigned_flag);
744 118 : context->set(context_index, *new_value);
745 : return true;
746 : }
747 : }
748 :
749 : // TODO(neis): Clean up context "extension" mess.
750 63 : if (!context->IsModuleContext() && context->has_extension()) {
751 10 : Handle<JSObject> ext(context->extension_object());
752 10 : Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
753 : DCHECK(maybe.IsJust());
754 10 : if (maybe.FromJust()) {
755 : // We don't expect this to do anything except replacing property value.
756 : JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value,
757 10 : NONE)
758 20 : .Check();
759 10 : return true;
760 : }
761 : }
762 :
763 : return false;
764 : }
765 :
766 145 : bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
767 : Handle<Object> new_value) {
768 290 : Handle<ScopeInfo> scope_info(GetFunction()->shared()->scope_info());
769 :
770 : // Parameter might be shadowed in context. Don't stop here.
771 145 : bool result = SetParameterValue(scope_info, variable_name, new_value);
772 :
773 : // Stack locals.
774 145 : if (SetStackVariableValue(scope_info, variable_name, new_value)) {
775 : return true;
776 : }
777 :
778 147 : if (scope_info->HasContext() &&
779 : SetContextVariableValue(scope_info, CurrentContext(), variable_name,
780 62 : new_value)) {
781 : return true;
782 : }
783 :
784 32 : return result;
785 : }
786 :
787 35 : bool ScopeIterator::SetModuleVariableValue(Handle<String> variable_name,
788 : Handle<Object> new_value) {
789 : DCHECK_NOT_NULL(frame_inspector_);
790 :
791 : // Get module context and its scope info.
792 35 : Handle<Context> context = CurrentContext();
793 70 : while (!context->IsModuleContext()) {
794 0 : context = handle(context->previous(), isolate_);
795 : }
796 70 : Handle<ScopeInfo> scope_info(context->scope_info(), isolate_);
797 : DCHECK_EQ(scope_info->scope_type(), MODULE_SCOPE);
798 :
799 35 : if (SetContextVariableValue(scope_info, context, variable_name, new_value)) {
800 : return true;
801 : }
802 :
803 : int cell_index;
804 : {
805 : VariableMode mode;
806 : InitializationFlag init_flag;
807 : MaybeAssignedFlag maybe_assigned_flag;
808 : cell_index = scope_info->ModuleIndex(variable_name, &mode, &init_flag,
809 25 : &maybe_assigned_flag);
810 : }
811 :
812 : // Setting imports is currently not supported.
813 25 : bool found = ModuleDescriptor::GetCellIndexKind(cell_index) ==
814 25 : ModuleDescriptor::kExport;
815 25 : if (found) {
816 : Module::StoreVariable(handle(context->module(), isolate_), cell_index,
817 30 : new_value);
818 : }
819 25 : return found;
820 : }
821 :
822 34 : bool ScopeIterator::SetInnerScopeVariableValue(Handle<String> variable_name,
823 : Handle<Object> new_value) {
824 34 : Handle<ScopeInfo> scope_info = CurrentScopeInfo();
825 : DCHECK(scope_info->scope_type() == BLOCK_SCOPE ||
826 : scope_info->scope_type() == EVAL_SCOPE);
827 :
828 : // Setting stack locals of optimized frames is not supported.
829 34 : if (SetStackVariableValue(scope_info, variable_name, new_value)) {
830 : return true;
831 : }
832 :
833 30 : if (HasContext() && SetContextVariableValue(scope_info, CurrentContext(),
834 15 : variable_name, new_value)) {
835 : return true;
836 : }
837 :
838 0 : return false;
839 : }
840 :
841 : // This method copies structure of MaterializeClosure method above.
842 50 : bool ScopeIterator::SetClosureVariableValue(Handle<String> variable_name,
843 : Handle<Object> new_value) {
844 : DCHECK(CurrentContext()->IsFunctionContext() ||
845 : CurrentContext()->IsEvalContext());
846 : return SetContextVariableValue(CurrentScopeInfo(), CurrentContext(),
847 50 : variable_name, new_value);
848 : }
849 :
850 34 : bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name,
851 : Handle<Object> new_value) {
852 : Handle<String> internalized_variable_name =
853 34 : isolate_->factory()->InternalizeString(variable_name);
854 34 : Handle<Context> context = CurrentContext();
855 : Handle<ScriptContextTable> script_contexts(
856 34 : context->global_object()->native_context()->script_context_table());
857 : ScriptContextTable::LookupResult lookup_result;
858 34 : if (ScriptContextTable::Lookup(script_contexts, internalized_variable_name,
859 : &lookup_result)) {
860 : Handle<Context> script_context = ScriptContextTable::GetContext(
861 34 : script_contexts, lookup_result.context_index);
862 68 : script_context->set(lookup_result.slot_index, *new_value);
863 : return true;
864 : }
865 :
866 : return false;
867 : }
868 :
869 19 : bool ScopeIterator::SetCatchVariableValue(Handle<String> variable_name,
870 : Handle<Object> new_value) {
871 19 : Handle<Context> context = CurrentContext();
872 : DCHECK(context->IsCatchContext());
873 19 : Handle<String> name(context->catch_name());
874 19 : if (!String::Equals(name, variable_name)) {
875 : return false;
876 : }
877 19 : context->set(Context::THROWN_OBJECT_INDEX, *new_value);
878 19 : return true;
879 : }
880 :
881 :
882 505757 : void ScopeIterator::CopyContextLocalsToScopeObject(
883 : Handle<ScopeInfo> scope_info, Handle<Context> context,
884 : Handle<JSObject> scope_object) {
885 : Isolate* isolate = scope_info->GetIsolate();
886 505757 : int local_count = scope_info->ContextLocalCount();
887 1011514 : if (local_count == 0) return;
888 : // Fill all context locals to the context extension.
889 1210068 : for (int i = 0; i < local_count; ++i) {
890 1210068 : Handle<String> name(scope_info->ContextLocalName(i));
891 1210068 : if (ScopeInfo::VariableIsSynthetic(*name)) continue;
892 947157 : int context_index = Context::MIN_CONTEXT_SLOTS + i;
893 : Handle<Object> value = Handle<Object>(context->get(context_index), isolate);
894 : // Reflect variables under TDZ as undefined in scope object.
895 947157 : if (value->IsTheHole(isolate)) continue;
896 : // This should always succeed.
897 : // TODO(verwaest): Use AddDataProperty instead.
898 941790 : JSObject::SetOwnPropertyIgnoreAttributes(scope_object, name, value, NONE)
899 1883580 : .Check();
900 : }
901 : }
902 :
903 2435 : void ScopeIterator::CopyModuleVarsToScopeObject(Handle<ScopeInfo> scope_info,
904 : Handle<Context> context,
905 : Handle<JSObject> scope_object) {
906 : Isolate* isolate = scope_info->GetIsolate();
907 :
908 : int module_variable_count =
909 : Smi::cast(scope_info->get(scope_info->ModuleVariableCountIndex()))
910 2435 : ->value();
911 7181 : for (int i = 0; i < module_variable_count; ++i) {
912 : Handle<String> local_name;
913 : Handle<Object> value;
914 : {
915 : String* name;
916 : int index;
917 4746 : scope_info->ModuleVariable(i, &name, &index);
918 4746 : CHECK(!ScopeInfo::VariableIsSynthetic(name));
919 4746 : local_name = handle(name, isolate);
920 14238 : value = Module::LoadVariable(handle(context->module(), isolate), index);
921 : }
922 :
923 : // Reflect variables under TDZ as undefined in scope object.
924 4746 : if (value->IsTheHole(isolate)) continue;
925 : // This should always succeed.
926 : // TODO(verwaest): Use AddDataProperty instead.
927 : JSObject::SetOwnPropertyIgnoreAttributes(scope_object, local_name, value,
928 635 : NONE)
929 1270 : .Check();
930 : }
931 2435 : }
932 :
933 21024 : void ScopeIterator::CopyContextExtensionToScopeObject(
934 : Handle<Context> context, Handle<JSObject> scope_object,
935 : KeyCollectionMode mode) {
936 42048 : if (context->extension_object() == nullptr) return;
937 347 : Handle<JSObject> extension(context->extension_object());
938 : Handle<FixedArray> keys =
939 347 : KeyAccumulator::GetKeys(extension, mode, ENUMERABLE_STRINGS)
940 694 : .ToHandleChecked();
941 :
942 1768 : for (int i = 0; i < keys->length(); i++) {
943 : // Names of variables introduced by eval are strings.
944 : DCHECK(keys->get(i)->IsString());
945 : Handle<String> key(String::cast(keys->get(i)));
946 : Handle<Object> value =
947 1074 : Object::GetPropertyOrElement(extension, key).ToHandleChecked();
948 537 : JSObject::SetOwnPropertyIgnoreAttributes(scope_object, key, value, NONE)
949 1074 : .Check();
950 : }
951 : }
952 :
953 628934 : void ScopeIterator::GetNestedScopeChain(Isolate* isolate, Scope* scope,
954 : int position) {
955 138267 : if (scope->is_function_scope()) {
956 : // Do not collect scopes of nested inner functions inside the current one.
957 : // Nested arrow functions could have the same end positions.
958 78826 : Handle<JSFunction> function = GetFunction();
959 78849 : if (scope->start_position() > function->shared()->start_position() &&
960 : scope->end_position() <= function->shared()->end_position()) {
961 : return;
962 : }
963 : }
964 138244 : if (scope->is_hidden()) {
965 : // We need to add this chain element in case the scope has a context
966 : // associated. We need to keep the scope chain and context chain in sync.
967 1457 : nested_scope_chain_.emplace_back(scope->scope_info());
968 : } else {
969 : nested_scope_chain_.emplace_back(
970 273574 : scope->scope_info(), scope->start_position(), scope->end_position());
971 : }
972 405134 : for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr;
973 : inner_scope = inner_scope->sibling()) {
974 : int beg_pos = inner_scope->start_position();
975 : int end_pos = inner_scope->end_position();
976 : DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden());
977 274695 : if (beg_pos <= position && position < end_pos) {
978 7805 : GetNestedScopeChain(isolate, inner_scope, position);
979 7805 : return;
980 : }
981 : }
982 : }
983 :
984 0 : bool ScopeIterator::HasNestedScopeChain() {
985 0 : return !nested_scope_chain_.empty();
986 : }
987 :
988 0 : ScopeIterator::ExtendedScopeInfo& ScopeIterator::LastNestedScopeChain() {
989 : DCHECK(HasNestedScopeChain());
990 0 : return nested_scope_chain_.back();
991 : }
992 :
993 : } // namespace internal
994 : } // namespace v8
|