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