Chromium Code Reviews| 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 |