| OLD | NEW |
| (Empty) |
| 1 // Copyright 2012 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/v8.h" | |
| 6 | |
| 7 #include "src/api.h" | |
| 8 #include "src/arguments.h" | |
| 9 #include "src/bootstrapper.h" | |
| 10 #include "src/code-stubs.h" | |
| 11 #include "src/codegen.h" | |
| 12 #include "src/compilation-cache.h" | |
| 13 #include "src/compiler.h" | |
| 14 #include "src/debug.h" | |
| 15 #include "src/deoptimizer.h" | |
| 16 #include "src/execution.h" | |
| 17 #include "src/full-codegen/full-codegen.h" | |
| 18 #include "src/global-handles.h" | |
| 19 #include "src/list.h" | |
| 20 #include "src/log.h" | |
| 21 #include "src/messages.h" | |
| 22 #include "src/snapshot/natives.h" | |
| 23 | |
| 24 #include "include/v8-debug.h" | |
| 25 | |
| 26 namespace v8 { | |
| 27 namespace internal { | |
| 28 | |
| 29 Debug::Debug(Isolate* isolate) | |
| 30 : debug_context_(Handle<Context>()), | |
| 31 event_listener_(Handle<Object>()), | |
| 32 event_listener_data_(Handle<Object>()), | |
| 33 message_handler_(NULL), | |
| 34 command_received_(0), | |
| 35 command_queue_(isolate->logger(), kQueueInitialSize), | |
| 36 is_active_(false), | |
| 37 is_suppressed_(false), | |
| 38 live_edit_enabled_(true), // TODO(yangguo): set to false by default. | |
| 39 break_disabled_(false), | |
| 40 in_debug_event_listener_(false), | |
| 41 break_on_exception_(false), | |
| 42 break_on_uncaught_exception_(false), | |
| 43 script_cache_(NULL), | |
| 44 debug_info_list_(NULL), | |
| 45 isolate_(isolate) { | |
| 46 ThreadInit(); | |
| 47 } | |
| 48 | |
| 49 | |
| 50 static v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) { | |
| 51 Handle<Context> context = isolate->debug()->debugger_entry()->GetContext(); | |
| 52 // Isolate::context() may have been NULL when "script collected" event | |
| 53 // occured. | |
| 54 if (context.is_null()) return v8::Local<v8::Context>(); | |
| 55 Handle<Context> native_context(context->native_context()); | |
| 56 return v8::Utils::ToLocal(native_context); | |
| 57 } | |
| 58 | |
| 59 | |
| 60 BreakLocation::BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo, | |
| 61 int position, int statement_position) | |
| 62 : debug_info_(debug_info), | |
| 63 pc_offset_(static_cast<int>(rinfo->pc() - debug_info->code()->entry())), | |
| 64 rmode_(rinfo->rmode()), | |
| 65 data_(rinfo->data()), | |
| 66 position_(position), | |
| 67 statement_position_(statement_position) {} | |
| 68 | |
| 69 | |
| 70 BreakLocation::Iterator::Iterator(Handle<DebugInfo> debug_info, | |
| 71 BreakLocatorType type) | |
| 72 : debug_info_(debug_info), | |
| 73 reloc_iterator_(debug_info->code(), GetModeMask(type)), | |
| 74 break_index_(-1), | |
| 75 position_(1), | |
| 76 statement_position_(1) { | |
| 77 if (!Done()) Next(); | |
| 78 } | |
| 79 | |
| 80 | |
| 81 int BreakLocation::Iterator::GetModeMask(BreakLocatorType type) { | |
| 82 int mask = 0; | |
| 83 mask |= RelocInfo::ModeMask(RelocInfo::POSITION); | |
| 84 mask |= RelocInfo::ModeMask(RelocInfo::STATEMENT_POSITION); | |
| 85 mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN); | |
| 86 mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL); | |
| 87 mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL); | |
| 88 if (type == ALL_BREAK_LOCATIONS) { | |
| 89 mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION); | |
| 90 mask |= RelocInfo::ModeMask(RelocInfo::DEBUGGER_STATEMENT); | |
| 91 } | |
| 92 return mask; | |
| 93 } | |
| 94 | |
| 95 | |
| 96 void BreakLocation::Iterator::Next() { | |
| 97 DisallowHeapAllocation no_gc; | |
| 98 DCHECK(!Done()); | |
| 99 | |
| 100 // Iterate through reloc info for code and original code stopping at each | |
| 101 // breakable code target. | |
| 102 bool first = break_index_ == -1; | |
| 103 while (!Done()) { | |
| 104 if (!first) reloc_iterator_.next(); | |
| 105 first = false; | |
| 106 if (Done()) return; | |
| 107 | |
| 108 // Whenever a statement position or (plain) position is passed update the | |
| 109 // current value of these. | |
| 110 if (RelocInfo::IsPosition(rmode())) { | |
| 111 if (RelocInfo::IsStatementPosition(rmode())) { | |
| 112 statement_position_ = static_cast<int>( | |
| 113 rinfo()->data() - debug_info_->shared()->start_position()); | |
| 114 } | |
| 115 // Always update the position as we don't want that to be before the | |
| 116 // statement position. | |
| 117 position_ = static_cast<int>(rinfo()->data() - | |
| 118 debug_info_->shared()->start_position()); | |
| 119 DCHECK(position_ >= 0); | |
| 120 DCHECK(statement_position_ >= 0); | |
| 121 continue; | |
| 122 } | |
| 123 | |
| 124 DCHECK(RelocInfo::IsDebugBreakSlot(rmode()) || | |
| 125 RelocInfo::IsDebuggerStatement(rmode())); | |
| 126 | |
| 127 if (RelocInfo::IsDebugBreakSlotAtReturn(rmode())) { | |
| 128 // Set the positions to the end of the function. | |
| 129 if (debug_info_->shared()->HasSourceCode()) { | |
| 130 position_ = debug_info_->shared()->end_position() - | |
| 131 debug_info_->shared()->start_position() - 1; | |
| 132 } else { | |
| 133 position_ = 0; | |
| 134 } | |
| 135 statement_position_ = position_; | |
| 136 } | |
| 137 | |
| 138 break; | |
| 139 } | |
| 140 break_index_++; | |
| 141 } | |
| 142 | |
| 143 | |
| 144 // Find the break point at the supplied address, or the closest one before | |
| 145 // the address. | |
| 146 BreakLocation BreakLocation::FromAddress(Handle<DebugInfo> debug_info, | |
| 147 BreakLocatorType type, Address pc) { | |
| 148 Iterator it(debug_info, type); | |
| 149 it.SkipTo(BreakIndexFromAddress(debug_info, type, pc)); | |
| 150 return it.GetBreakLocation(); | |
| 151 } | |
| 152 | |
| 153 | |
| 154 // Find the break point at the supplied address, or the closest one before | |
| 155 // the address. | |
| 156 void BreakLocation::FromAddressSameStatement(Handle<DebugInfo> debug_info, | |
| 157 BreakLocatorType type, Address pc, | |
| 158 List<BreakLocation>* result_out) { | |
| 159 int break_index = BreakIndexFromAddress(debug_info, type, pc); | |
| 160 Iterator it(debug_info, type); | |
| 161 it.SkipTo(break_index); | |
| 162 int statement_position = it.statement_position(); | |
| 163 while (!it.Done() && it.statement_position() == statement_position) { | |
| 164 result_out->Add(it.GetBreakLocation()); | |
| 165 it.Next(); | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 | |
| 170 int BreakLocation::BreakIndexFromAddress(Handle<DebugInfo> debug_info, | |
| 171 BreakLocatorType type, Address pc) { | |
| 172 // Run through all break points to locate the one closest to the address. | |
| 173 int closest_break = 0; | |
| 174 int distance = kMaxInt; | |
| 175 for (Iterator it(debug_info, type); !it.Done(); it.Next()) { | |
| 176 // Check if this break point is closer that what was previously found. | |
| 177 if (it.pc() <= pc && pc - it.pc() < distance) { | |
| 178 closest_break = it.break_index(); | |
| 179 distance = static_cast<int>(pc - it.pc()); | |
| 180 // Check whether we can't get any closer. | |
| 181 if (distance == 0) break; | |
| 182 } | |
| 183 } | |
| 184 return closest_break; | |
| 185 } | |
| 186 | |
| 187 | |
| 188 BreakLocation BreakLocation::FromPosition(Handle<DebugInfo> debug_info, | |
| 189 BreakLocatorType type, int position, | |
| 190 BreakPositionAlignment alignment) { | |
| 191 // Run through all break points to locate the one closest to the source | |
| 192 // position. | |
| 193 int closest_break = 0; | |
| 194 int distance = kMaxInt; | |
| 195 | |
| 196 for (Iterator it(debug_info, type); !it.Done(); it.Next()) { | |
| 197 int next_position; | |
| 198 if (alignment == STATEMENT_ALIGNED) { | |
| 199 next_position = it.statement_position(); | |
| 200 } else { | |
| 201 DCHECK(alignment == BREAK_POSITION_ALIGNED); | |
| 202 next_position = it.position(); | |
| 203 } | |
| 204 if (position <= next_position && next_position - position < distance) { | |
| 205 closest_break = it.break_index(); | |
| 206 distance = next_position - position; | |
| 207 // Check whether we can't get any closer. | |
| 208 if (distance == 0) break; | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 Iterator it(debug_info, type); | |
| 213 it.SkipTo(closest_break); | |
| 214 return it.GetBreakLocation(); | |
| 215 } | |
| 216 | |
| 217 | |
| 218 void BreakLocation::SetBreakPoint(Handle<Object> break_point_object) { | |
| 219 // If there is not already a real break point here patch code with debug | |
| 220 // break. | |
| 221 if (!HasBreakPoint()) SetDebugBreak(); | |
| 222 DCHECK(IsDebugBreak() || IsDebuggerStatement()); | |
| 223 // Set the break point information. | |
| 224 DebugInfo::SetBreakPoint(debug_info_, pc_offset_, position_, | |
| 225 statement_position_, break_point_object); | |
| 226 } | |
| 227 | |
| 228 | |
| 229 void BreakLocation::ClearBreakPoint(Handle<Object> break_point_object) { | |
| 230 // Clear the break point information. | |
| 231 DebugInfo::ClearBreakPoint(debug_info_, pc_offset_, break_point_object); | |
| 232 // If there are no more break points here remove the debug break. | |
| 233 if (!HasBreakPoint()) { | |
| 234 ClearDebugBreak(); | |
| 235 DCHECK(!IsDebugBreak()); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 | |
| 240 void BreakLocation::SetOneShot() { | |
| 241 // Debugger statement always calls debugger. No need to modify it. | |
| 242 if (IsDebuggerStatement()) return; | |
| 243 | |
| 244 // If there is a real break point here no more to do. | |
| 245 if (HasBreakPoint()) { | |
| 246 DCHECK(IsDebugBreak()); | |
| 247 return; | |
| 248 } | |
| 249 | |
| 250 // Patch code with debug break. | |
| 251 SetDebugBreak(); | |
| 252 } | |
| 253 | |
| 254 | |
| 255 void BreakLocation::ClearOneShot() { | |
| 256 // Debugger statement always calls debugger. No need to modify it. | |
| 257 if (IsDebuggerStatement()) return; | |
| 258 | |
| 259 // If there is a real break point here no more to do. | |
| 260 if (HasBreakPoint()) { | |
| 261 DCHECK(IsDebugBreak()); | |
| 262 return; | |
| 263 } | |
| 264 | |
| 265 // Patch code removing debug break. | |
| 266 ClearDebugBreak(); | |
| 267 DCHECK(!IsDebugBreak()); | |
| 268 } | |
| 269 | |
| 270 | |
| 271 void BreakLocation::SetDebugBreak() { | |
| 272 // Debugger statement always calls debugger. No need to modify it. | |
| 273 if (IsDebuggerStatement()) return; | |
| 274 | |
| 275 // If there is already a break point here just return. This might happen if | |
| 276 // the same code is flooded with break points twice. Flooding the same | |
| 277 // function twice might happen when stepping in a function with an exception | |
| 278 // handler as the handler and the function is the same. | |
| 279 if (IsDebugBreak()) return; | |
| 280 | |
| 281 DCHECK(IsDebugBreakSlot()); | |
| 282 Builtins* builtins = debug_info_->GetIsolate()->builtins(); | |
| 283 Handle<Code> target = | |
| 284 IsReturn() ? builtins->Return_DebugBreak() : builtins->Slot_DebugBreak(); | |
| 285 DebugCodegen::PatchDebugBreakSlot(pc(), target); | |
| 286 DCHECK(IsDebugBreak()); | |
| 287 } | |
| 288 | |
| 289 | |
| 290 void BreakLocation::ClearDebugBreak() { | |
| 291 // Debugger statement always calls debugger. No need to modify it. | |
| 292 if (IsDebuggerStatement()) return; | |
| 293 | |
| 294 DCHECK(IsDebugBreakSlot()); | |
| 295 DebugCodegen::ClearDebugBreakSlot(pc()); | |
| 296 DCHECK(!IsDebugBreak()); | |
| 297 } | |
| 298 | |
| 299 | |
| 300 bool BreakLocation::IsStepInLocation() const { | |
| 301 return IsConstructCall() || IsCall(); | |
| 302 } | |
| 303 | |
| 304 | |
| 305 bool BreakLocation::IsDebugBreak() const { | |
| 306 if (IsDebugBreakSlot()) { | |
| 307 return rinfo().IsPatchedDebugBreakSlotSequence(); | |
| 308 } | |
| 309 return false; | |
| 310 } | |
| 311 | |
| 312 | |
| 313 Handle<Object> BreakLocation::BreakPointObjects() const { | |
| 314 return debug_info_->GetBreakPointObjects(pc_offset_); | |
| 315 } | |
| 316 | |
| 317 | |
| 318 // Threading support. | |
| 319 void Debug::ThreadInit() { | |
| 320 thread_local_.break_count_ = 0; | |
| 321 thread_local_.break_id_ = 0; | |
| 322 thread_local_.break_frame_id_ = StackFrame::NO_ID; | |
| 323 thread_local_.last_step_action_ = StepNone; | |
| 324 thread_local_.last_statement_position_ = RelocInfo::kNoPosition; | |
| 325 thread_local_.step_count_ = 0; | |
| 326 thread_local_.last_fp_ = 0; | |
| 327 thread_local_.queued_step_count_ = 0; | |
| 328 thread_local_.step_into_fp_ = 0; | |
| 329 thread_local_.step_out_fp_ = 0; | |
| 330 // TODO(isolates): frames_are_dropped_? | |
| 331 base::NoBarrier_Store(&thread_local_.current_debug_scope_, | |
| 332 static_cast<base::AtomicWord>(0)); | |
| 333 thread_local_.restarter_frame_function_pointer_ = NULL; | |
| 334 } | |
| 335 | |
| 336 | |
| 337 char* Debug::ArchiveDebug(char* storage) { | |
| 338 char* to = storage; | |
| 339 MemCopy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal)); | |
| 340 ThreadInit(); | |
| 341 return storage + ArchiveSpacePerThread(); | |
| 342 } | |
| 343 | |
| 344 | |
| 345 char* Debug::RestoreDebug(char* storage) { | |
| 346 char* from = storage; | |
| 347 MemCopy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal)); | |
| 348 return storage + ArchiveSpacePerThread(); | |
| 349 } | |
| 350 | |
| 351 | |
| 352 int Debug::ArchiveSpacePerThread() { | |
| 353 return sizeof(ThreadLocal); | |
| 354 } | |
| 355 | |
| 356 | |
| 357 ScriptCache::ScriptCache(Isolate* isolate) : isolate_(isolate) { | |
| 358 Heap* heap = isolate_->heap(); | |
| 359 HandleScope scope(isolate_); | |
| 360 | |
| 361 DCHECK(isolate_->debug()->is_active()); | |
| 362 | |
| 363 // Perform a GC to get rid of all unreferenced scripts. | |
| 364 heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "ScriptCache"); | |
| 365 | |
| 366 // Scan heap for Script objects. | |
| 367 List<Handle<Script> > scripts; | |
| 368 { | |
| 369 HeapIterator iterator(heap, HeapIterator::kFilterUnreachable); | |
| 370 DisallowHeapAllocation no_allocation; | |
| 371 for (HeapObject* obj = iterator.next(); obj != NULL; | |
| 372 obj = iterator.next()) { | |
| 373 if (obj->IsScript() && Script::cast(obj)->HasValidSource()) { | |
| 374 scripts.Add(Handle<Script>(Script::cast(obj))); | |
| 375 } | |
| 376 } | |
| 377 } | |
| 378 | |
| 379 GlobalHandles* global_handles = isolate_->global_handles(); | |
| 380 table_ = Handle<WeakValueHashTable>::cast(global_handles->Create( | |
| 381 Object::cast(*WeakValueHashTable::New(isolate_, scripts.length())))); | |
| 382 for (int i = 0; i < scripts.length(); i++) Add(scripts[i]); | |
| 383 } | |
| 384 | |
| 385 | |
| 386 void ScriptCache::Add(Handle<Script> script) { | |
| 387 HandleScope scope(isolate_); | |
| 388 Handle<Smi> id(script->id(), isolate_); | |
| 389 | |
| 390 #ifdef DEBUG | |
| 391 Handle<Object> lookup(table_->LookupWeak(id), isolate_); | |
| 392 if (!lookup->IsTheHole()) { | |
| 393 Handle<Script> found = Handle<Script>::cast(lookup); | |
| 394 DCHECK(script->id() == found->id()); | |
| 395 DCHECK(!script->name()->IsString() || | |
| 396 String::cast(script->name())->Equals(String::cast(found->name()))); | |
| 397 } | |
| 398 #endif | |
| 399 | |
| 400 Handle<WeakValueHashTable> new_table = | |
| 401 WeakValueHashTable::PutWeak(table_, id, script); | |
| 402 | |
| 403 if (new_table.is_identical_to(table_)) return; | |
| 404 GlobalHandles* global_handles = isolate_->global_handles(); | |
| 405 global_handles->Destroy(Handle<Object>::cast(table_).location()); | |
| 406 table_ = Handle<WeakValueHashTable>::cast( | |
| 407 global_handles->Create(Object::cast(*new_table))); | |
| 408 } | |
| 409 | |
| 410 | |
| 411 ScriptCache::~ScriptCache() { | |
| 412 isolate_->global_handles()->Destroy(Handle<Object>::cast(table_).location()); | |
| 413 table_ = Handle<WeakValueHashTable>(); | |
| 414 } | |
| 415 | |
| 416 | |
| 417 DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) { | |
| 418 // Globalize the request debug info object and make it weak. | |
| 419 GlobalHandles* global_handles = debug_info->GetIsolate()->global_handles(); | |
| 420 debug_info_ = | |
| 421 Handle<DebugInfo>::cast(global_handles->Create(debug_info)).location(); | |
| 422 } | |
| 423 | |
| 424 | |
| 425 DebugInfoListNode::~DebugInfoListNode() { | |
| 426 if (debug_info_ == nullptr) return; | |
| 427 GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_info_)); | |
| 428 debug_info_ = nullptr; | |
| 429 } | |
| 430 | |
| 431 | |
| 432 bool Debug::CompileDebuggerScript(Isolate* isolate, int index) { | |
| 433 Factory* factory = isolate->factory(); | |
| 434 HandleScope scope(isolate); | |
| 435 | |
| 436 // Bail out if the index is invalid. | |
| 437 if (index == -1) return false; | |
| 438 | |
| 439 // Find source and name for the requested script. | |
| 440 Handle<String> source_code = | |
| 441 isolate->bootstrapper()->SourceLookup<Natives>(index); | |
| 442 Vector<const char> name = Natives::GetScriptName(index); | |
| 443 Handle<String> script_name = | |
| 444 factory->NewStringFromAscii(name).ToHandleChecked(); | |
| 445 Handle<Context> context = isolate->native_context(); | |
| 446 | |
| 447 // Compile the script. | |
| 448 Handle<SharedFunctionInfo> function_info; | |
| 449 function_info = Compiler::CompileScript( | |
| 450 source_code, script_name, 0, 0, ScriptOriginOptions(), Handle<Object>(), | |
| 451 context, NULL, NULL, ScriptCompiler::kNoCompileOptions, NATIVES_CODE, | |
| 452 false); | |
| 453 if (function_info.is_null()) return false; | |
| 454 | |
| 455 // Execute the shared function in the debugger context. | |
| 456 Handle<JSFunction> function = | |
| 457 factory->NewFunctionFromSharedFunctionInfo(function_info, context); | |
| 458 | |
| 459 MaybeHandle<Object> maybe_exception; | |
| 460 MaybeHandle<Object> result = Execution::TryCall( | |
| 461 function, handle(context->global_proxy()), 0, NULL, &maybe_exception); | |
| 462 | |
| 463 // Check for caught exceptions. | |
| 464 if (result.is_null()) { | |
| 465 DCHECK(!isolate->has_pending_exception()); | |
| 466 MessageLocation computed_location; | |
| 467 isolate->ComputeLocation(&computed_location); | |
| 468 Handle<JSMessageObject> message = MessageHandler::MakeMessageObject( | |
| 469 isolate, MessageTemplate::kDebuggerLoading, &computed_location, | |
| 470 isolate->factory()->undefined_value(), Handle<JSArray>()); | |
| 471 DCHECK(!isolate->has_pending_exception()); | |
| 472 Handle<Object> exception; | |
| 473 if (maybe_exception.ToHandle(&exception)) { | |
| 474 isolate->set_pending_exception(*exception); | |
| 475 MessageHandler::ReportMessage(isolate, NULL, message); | |
| 476 } | |
| 477 DCHECK(!maybe_exception.is_null()); | |
| 478 return false; | |
| 479 } | |
| 480 | |
| 481 // Mark this script as native and return successfully. | |
| 482 Handle<Script> script(Script::cast(function->shared()->script())); | |
| 483 script->set_type(Smi::FromInt(Script::TYPE_NATIVE)); | |
| 484 return true; | |
| 485 } | |
| 486 | |
| 487 | |
| 488 bool Debug::Load() { | |
| 489 // Return if debugger is already loaded. | |
| 490 if (is_loaded()) return true; | |
| 491 | |
| 492 // Bail out if we're already in the process of compiling the native | |
| 493 // JavaScript source code for the debugger. | |
| 494 if (is_suppressed_) return false; | |
| 495 SuppressDebug while_loading(this); | |
| 496 | |
| 497 // Disable breakpoints and interrupts while compiling and running the | |
| 498 // debugger scripts including the context creation code. | |
| 499 DisableBreak disable(this, true); | |
| 500 PostponeInterruptsScope postpone(isolate_); | |
| 501 | |
| 502 // Create the debugger context. | |
| 503 HandleScope scope(isolate_); | |
| 504 ExtensionConfiguration no_extensions; | |
| 505 Handle<Context> context = isolate_->bootstrapper()->CreateEnvironment( | |
| 506 MaybeHandle<JSGlobalProxy>(), v8::Local<ObjectTemplate>(), | |
| 507 &no_extensions); | |
| 508 | |
| 509 // Fail if no context could be created. | |
| 510 if (context.is_null()) return false; | |
| 511 | |
| 512 // Use the debugger context. | |
| 513 SaveContext save(isolate_); | |
| 514 isolate_->set_context(*context); | |
| 515 | |
| 516 // Expose the builtins object in the debugger context. | |
| 517 Handle<String> key = isolate_->factory()->InternalizeOneByteString( | |
| 518 STATIC_CHAR_VECTOR("builtins")); | |
| 519 Handle<GlobalObject> global = | |
| 520 Handle<GlobalObject>(context->global_object(), isolate_); | |
| 521 Handle<JSBuiltinsObject> builtin = | |
| 522 Handle<JSBuiltinsObject>(global->builtins(), isolate_); | |
| 523 RETURN_ON_EXCEPTION_VALUE( | |
| 524 isolate_, Object::SetProperty(global, key, builtin, SLOPPY), false); | |
| 525 | |
| 526 // Compile the JavaScript for the debugger in the debugger context. | |
| 527 bool caught_exception = | |
| 528 !CompileDebuggerScript(isolate_, Natives::GetIndex("mirror")) || | |
| 529 !CompileDebuggerScript(isolate_, Natives::GetIndex("debug")); | |
| 530 | |
| 531 if (FLAG_enable_liveedit) { | |
| 532 caught_exception = caught_exception || | |
| 533 !CompileDebuggerScript(isolate_, Natives::GetIndex("liveedit")); | |
| 534 } | |
| 535 // Check for caught exceptions. | |
| 536 if (caught_exception) return false; | |
| 537 | |
| 538 debug_context_ = Handle<Context>::cast( | |
| 539 isolate_->global_handles()->Create(*context)); | |
| 540 return true; | |
| 541 } | |
| 542 | |
| 543 | |
| 544 void Debug::Unload() { | |
| 545 ClearAllBreakPoints(); | |
| 546 ClearStepping(); | |
| 547 | |
| 548 // Return debugger is not loaded. | |
| 549 if (!is_loaded()) return; | |
| 550 | |
| 551 // Clear the script cache. | |
| 552 if (script_cache_ != NULL) { | |
| 553 delete script_cache_; | |
| 554 script_cache_ = NULL; | |
| 555 } | |
| 556 | |
| 557 // Clear debugger context global handle. | |
| 558 GlobalHandles::Destroy(Handle<Object>::cast(debug_context_).location()); | |
| 559 debug_context_ = Handle<Context>(); | |
| 560 } | |
| 561 | |
| 562 | |
| 563 void Debug::Break(Arguments args, JavaScriptFrame* frame) { | |
| 564 Heap* heap = isolate_->heap(); | |
| 565 HandleScope scope(isolate_); | |
| 566 DCHECK(args.length() == 0); | |
| 567 | |
| 568 // Initialize LiveEdit. | |
| 569 LiveEdit::InitializeThreadLocal(this); | |
| 570 | |
| 571 // Just continue if breaks are disabled or debugger cannot be loaded. | |
| 572 if (break_disabled()) return; | |
| 573 | |
| 574 // Enter the debugger. | |
| 575 DebugScope debug_scope(this); | |
| 576 if (debug_scope.failed()) return; | |
| 577 | |
| 578 // Postpone interrupt during breakpoint processing. | |
| 579 PostponeInterruptsScope postpone(isolate_); | |
| 580 | |
| 581 // Get the debug info (create it if it does not exist). | |
| 582 Handle<SharedFunctionInfo> shared = | |
| 583 Handle<SharedFunctionInfo>(frame->function()->shared()); | |
| 584 Handle<DebugInfo> debug_info(shared->GetDebugInfo()); | |
| 585 | |
| 586 // Find the break point where execution has stopped. | |
| 587 // PC points to the instruction after the current one, possibly a break | |
| 588 // location as well. So the "- 1" to exclude it from the search. | |
| 589 Address call_pc = frame->pc() - 1; | |
| 590 BreakLocation break_location = | |
| 591 BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc); | |
| 592 | |
| 593 // Check whether step next reached a new statement. | |
| 594 if (!StepNextContinue(&break_location, frame)) { | |
| 595 // Decrease steps left if performing multiple steps. | |
| 596 if (thread_local_.step_count_ > 0) { | |
| 597 thread_local_.step_count_--; | |
| 598 } | |
| 599 } | |
| 600 | |
| 601 // If there is one or more real break points check whether any of these are | |
| 602 // triggered. | |
| 603 Handle<Object> break_points_hit(heap->undefined_value(), isolate_); | |
| 604 if (break_location.HasBreakPoint()) { | |
| 605 Handle<Object> break_point_objects = break_location.BreakPointObjects(); | |
| 606 break_points_hit = CheckBreakPoints(break_point_objects); | |
| 607 } | |
| 608 | |
| 609 // If step out is active skip everything until the frame where we need to step | |
| 610 // out to is reached, unless real breakpoint is hit. | |
| 611 if (StepOutActive() && | |
| 612 frame->fp() != thread_local_.step_out_fp_ && | |
| 613 break_points_hit->IsUndefined() ) { | |
| 614 // Step count should always be 0 for StepOut. | |
| 615 DCHECK(thread_local_.step_count_ == 0); | |
| 616 } else if (!break_points_hit->IsUndefined() || | |
| 617 (thread_local_.last_step_action_ != StepNone && | |
| 618 thread_local_.step_count_ == 0)) { | |
| 619 // Notify debugger if a real break point is triggered or if performing | |
| 620 // single stepping with no more steps to perform. Otherwise do another step. | |
| 621 | |
| 622 // Clear all current stepping setup. | |
| 623 ClearStepping(); | |
| 624 | |
| 625 if (thread_local_.queued_step_count_ > 0) { | |
| 626 // Perform queued steps | |
| 627 int step_count = thread_local_.queued_step_count_; | |
| 628 | |
| 629 // Clear queue | |
| 630 thread_local_.queued_step_count_ = 0; | |
| 631 | |
| 632 PrepareStep(StepNext, step_count, StackFrame::NO_ID); | |
| 633 } else { | |
| 634 // Notify the debug event listeners. | |
| 635 OnDebugBreak(break_points_hit, false); | |
| 636 } | |
| 637 } else if (thread_local_.last_step_action_ != StepNone) { | |
| 638 // Hold on to last step action as it is cleared by the call to | |
| 639 // ClearStepping. | |
| 640 StepAction step_action = thread_local_.last_step_action_; | |
| 641 int step_count = thread_local_.step_count_; | |
| 642 | |
| 643 // If StepNext goes deeper in code, StepOut until original frame | |
| 644 // and keep step count queued up in the meantime. | |
| 645 if (step_action == StepNext && frame->fp() < thread_local_.last_fp_) { | |
| 646 // Count frames until target frame | |
| 647 int count = 0; | |
| 648 JavaScriptFrameIterator it(isolate_); | |
| 649 while (!it.done() && it.frame()->fp() < thread_local_.last_fp_) { | |
| 650 count++; | |
| 651 it.Advance(); | |
| 652 } | |
| 653 | |
| 654 // Check that we indeed found the frame we are looking for. | |
| 655 CHECK(!it.done() && (it.frame()->fp() == thread_local_.last_fp_)); | |
| 656 if (step_count > 1) { | |
| 657 // Save old count and action to continue stepping after StepOut. | |
| 658 thread_local_.queued_step_count_ = step_count - 1; | |
| 659 } | |
| 660 | |
| 661 // Set up for StepOut to reach target frame. | |
| 662 step_action = StepOut; | |
| 663 step_count = count; | |
| 664 } | |
| 665 | |
| 666 // Clear all current stepping setup. | |
| 667 ClearStepping(); | |
| 668 | |
| 669 // Set up for the remaining steps. | |
| 670 PrepareStep(step_action, step_count, StackFrame::NO_ID); | |
| 671 } | |
| 672 } | |
| 673 | |
| 674 | |
| 675 // Check the break point objects for whether one or more are actually | |
| 676 // triggered. This function returns a JSArray with the break point objects | |
| 677 // which is triggered. | |
| 678 Handle<Object> Debug::CheckBreakPoints(Handle<Object> break_point_objects) { | |
| 679 Factory* factory = isolate_->factory(); | |
| 680 | |
| 681 // Count the number of break points hit. If there are multiple break points | |
| 682 // they are in a FixedArray. | |
| 683 Handle<FixedArray> break_points_hit; | |
| 684 int break_points_hit_count = 0; | |
| 685 DCHECK(!break_point_objects->IsUndefined()); | |
| 686 if (break_point_objects->IsFixedArray()) { | |
| 687 Handle<FixedArray> array(FixedArray::cast(*break_point_objects)); | |
| 688 break_points_hit = factory->NewFixedArray(array->length()); | |
| 689 for (int i = 0; i < array->length(); i++) { | |
| 690 Handle<Object> o(array->get(i), isolate_); | |
| 691 if (CheckBreakPoint(o)) { | |
| 692 break_points_hit->set(break_points_hit_count++, *o); | |
| 693 } | |
| 694 } | |
| 695 } else { | |
| 696 break_points_hit = factory->NewFixedArray(1); | |
| 697 if (CheckBreakPoint(break_point_objects)) { | |
| 698 break_points_hit->set(break_points_hit_count++, *break_point_objects); | |
| 699 } | |
| 700 } | |
| 701 | |
| 702 // Return undefined if no break points were triggered. | |
| 703 if (break_points_hit_count == 0) { | |
| 704 return factory->undefined_value(); | |
| 705 } | |
| 706 // Return break points hit as a JSArray. | |
| 707 Handle<JSArray> result = factory->NewJSArrayWithElements(break_points_hit); | |
| 708 result->set_length(Smi::FromInt(break_points_hit_count)); | |
| 709 return result; | |
| 710 } | |
| 711 | |
| 712 | |
| 713 // Check whether a single break point object is triggered. | |
| 714 bool Debug::CheckBreakPoint(Handle<Object> break_point_object) { | |
| 715 Factory* factory = isolate_->factory(); | |
| 716 HandleScope scope(isolate_); | |
| 717 | |
| 718 // Ignore check if break point object is not a JSObject. | |
| 719 if (!break_point_object->IsJSObject()) return true; | |
| 720 | |
| 721 // Get the function IsBreakPointTriggered (defined in debug-debugger.js). | |
| 722 Handle<String> is_break_point_triggered_string = | |
| 723 factory->InternalizeOneByteString( | |
| 724 STATIC_CHAR_VECTOR("IsBreakPointTriggered")); | |
| 725 Handle<GlobalObject> debug_global(debug_context()->global_object()); | |
| 726 Handle<JSFunction> check_break_point = | |
| 727 Handle<JSFunction>::cast(Object::GetProperty( | |
| 728 debug_global, is_break_point_triggered_string).ToHandleChecked()); | |
| 729 | |
| 730 // Get the break id as an object. | |
| 731 Handle<Object> break_id = factory->NewNumberFromInt(Debug::break_id()); | |
| 732 | |
| 733 // Call HandleBreakPointx. | |
| 734 Handle<Object> argv[] = { break_id, break_point_object }; | |
| 735 Handle<Object> result; | |
| 736 if (!Execution::TryCall(check_break_point, | |
| 737 isolate_->js_builtins_object(), | |
| 738 arraysize(argv), | |
| 739 argv).ToHandle(&result)) { | |
| 740 return false; | |
| 741 } | |
| 742 | |
| 743 // Return whether the break point is triggered. | |
| 744 return result->IsTrue(); | |
| 745 } | |
| 746 | |
| 747 | |
| 748 bool Debug::SetBreakPoint(Handle<JSFunction> function, | |
| 749 Handle<Object> break_point_object, | |
| 750 int* source_position) { | |
| 751 HandleScope scope(isolate_); | |
| 752 | |
| 753 // Make sure the function is compiled and has set up the debug info. | |
| 754 Handle<SharedFunctionInfo> shared(function->shared()); | |
| 755 if (!EnsureDebugInfo(shared, function)) { | |
| 756 // Return if retrieving debug info failed. | |
| 757 return true; | |
| 758 } | |
| 759 | |
| 760 Handle<DebugInfo> debug_info(shared->GetDebugInfo()); | |
| 761 // Source positions starts with zero. | |
| 762 DCHECK(*source_position >= 0); | |
| 763 | |
| 764 // Find the break point and change it. | |
| 765 BreakLocation location = BreakLocation::FromPosition( | |
| 766 debug_info, ALL_BREAK_LOCATIONS, *source_position, STATEMENT_ALIGNED); | |
| 767 *source_position = location.statement_position(); | |
| 768 location.SetBreakPoint(break_point_object); | |
| 769 | |
| 770 // At least one active break point now. | |
| 771 return debug_info->GetBreakPointCount() > 0; | |
| 772 } | |
| 773 | |
| 774 | |
| 775 bool Debug::SetBreakPointForScript(Handle<Script> script, | |
| 776 Handle<Object> break_point_object, | |
| 777 int* source_position, | |
| 778 BreakPositionAlignment alignment) { | |
| 779 HandleScope scope(isolate_); | |
| 780 | |
| 781 // Obtain shared function info for the function. | |
| 782 Handle<Object> result = | |
| 783 FindSharedFunctionInfoInScript(script, *source_position); | |
| 784 if (result->IsUndefined()) return false; | |
| 785 | |
| 786 // Make sure the function has set up the debug info. | |
| 787 Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result); | |
| 788 if (!EnsureDebugInfo(shared, Handle<JSFunction>::null())) { | |
| 789 // Return if retrieving debug info failed. | |
| 790 return false; | |
| 791 } | |
| 792 | |
| 793 // Find position within function. The script position might be before the | |
| 794 // source position of the first function. | |
| 795 int position; | |
| 796 if (shared->start_position() > *source_position) { | |
| 797 position = 0; | |
| 798 } else { | |
| 799 position = *source_position - shared->start_position(); | |
| 800 } | |
| 801 | |
| 802 Handle<DebugInfo> debug_info(shared->GetDebugInfo()); | |
| 803 // Source positions starts with zero. | |
| 804 DCHECK(position >= 0); | |
| 805 | |
| 806 // Find the break point and change it. | |
| 807 BreakLocation location = BreakLocation::FromPosition( | |
| 808 debug_info, ALL_BREAK_LOCATIONS, position, alignment); | |
| 809 location.SetBreakPoint(break_point_object); | |
| 810 | |
| 811 position = (alignment == STATEMENT_ALIGNED) ? location.statement_position() | |
| 812 : location.position(); | |
| 813 | |
| 814 *source_position = position + shared->start_position(); | |
| 815 | |
| 816 // At least one active break point now. | |
| 817 DCHECK(debug_info->GetBreakPointCount() > 0); | |
| 818 return true; | |
| 819 } | |
| 820 | |
| 821 | |
| 822 void Debug::ClearBreakPoint(Handle<Object> break_point_object) { | |
| 823 HandleScope scope(isolate_); | |
| 824 | |
| 825 DebugInfoListNode* node = debug_info_list_; | |
| 826 while (node != NULL) { | |
| 827 Handle<Object> result = | |
| 828 DebugInfo::FindBreakPointInfo(node->debug_info(), break_point_object); | |
| 829 if (!result->IsUndefined()) { | |
| 830 // Get information in the break point. | |
| 831 Handle<BreakPointInfo> break_point_info = | |
| 832 Handle<BreakPointInfo>::cast(result); | |
| 833 Handle<DebugInfo> debug_info = node->debug_info(); | |
| 834 | |
| 835 // Find the break point and clear it. | |
| 836 Address pc = debug_info->code()->entry() + | |
| 837 break_point_info->code_position()->value(); | |
| 838 | |
| 839 BreakLocation location = | |
| 840 BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, pc); | |
| 841 location.ClearBreakPoint(break_point_object); | |
| 842 | |
| 843 // If there are no more break points left remove the debug info for this | |
| 844 // function. | |
| 845 if (debug_info->GetBreakPointCount() == 0) { | |
| 846 RemoveDebugInfoAndClearFromShared(debug_info); | |
| 847 } | |
| 848 | |
| 849 return; | |
| 850 } | |
| 851 node = node->next(); | |
| 852 } | |
| 853 } | |
| 854 | |
| 855 | |
| 856 // Clear out all the debug break code. This is ONLY supposed to be used when | |
| 857 // shutting down the debugger as it will leave the break point information in | |
| 858 // DebugInfo even though the code is patched back to the non break point state. | |
| 859 void Debug::ClearAllBreakPoints() { | |
| 860 for (DebugInfoListNode* node = debug_info_list_; node != NULL; | |
| 861 node = node->next()) { | |
| 862 for (BreakLocation::Iterator it(node->debug_info(), ALL_BREAK_LOCATIONS); | |
| 863 !it.Done(); it.Next()) { | |
| 864 it.GetBreakLocation().ClearDebugBreak(); | |
| 865 } | |
| 866 } | |
| 867 // Remove all debug info. | |
| 868 while (debug_info_list_ != NULL) { | |
| 869 RemoveDebugInfoAndClearFromShared(debug_info_list_->debug_info()); | |
| 870 } | |
| 871 } | |
| 872 | |
| 873 | |
| 874 void Debug::FloodWithOneShot(Handle<JSFunction> function, | |
| 875 BreakLocatorType type) { | |
| 876 // Make sure the function is compiled and has set up the debug info. | |
| 877 Handle<SharedFunctionInfo> shared(function->shared()); | |
| 878 if (!EnsureDebugInfo(shared, function)) { | |
| 879 // Return if we failed to retrieve the debug info. | |
| 880 return; | |
| 881 } | |
| 882 | |
| 883 // Flood the function with break points. | |
| 884 Handle<DebugInfo> debug_info(shared->GetDebugInfo()); | |
| 885 for (BreakLocation::Iterator it(debug_info, type); !it.Done(); it.Next()) { | |
| 886 it.GetBreakLocation().SetOneShot(); | |
| 887 } | |
| 888 } | |
| 889 | |
| 890 | |
| 891 void Debug::FloodBoundFunctionWithOneShot(Handle<JSFunction> function) { | |
| 892 Handle<FixedArray> new_bindings(function->function_bindings()); | |
| 893 Handle<Object> bindee(new_bindings->get(JSFunction::kBoundFunctionIndex), | |
| 894 isolate_); | |
| 895 | |
| 896 if (!bindee.is_null() && bindee->IsJSFunction()) { | |
| 897 Handle<JSFunction> bindee_function(JSFunction::cast(*bindee)); | |
| 898 FloodWithOneShotGeneric(bindee_function); | |
| 899 } | |
| 900 } | |
| 901 | |
| 902 | |
| 903 void Debug::FloodDefaultConstructorWithOneShot(Handle<JSFunction> function) { | |
| 904 DCHECK(function->shared()->is_default_constructor()); | |
| 905 // Instead of stepping into the function we directly step into the super class | |
| 906 // constructor. | |
| 907 Isolate* isolate = function->GetIsolate(); | |
| 908 PrototypeIterator iter(isolate, function); | |
| 909 Handle<Object> proto = PrototypeIterator::GetCurrent(iter); | |
| 910 if (!proto->IsJSFunction()) return; // Object.prototype | |
| 911 Handle<JSFunction> function_proto = Handle<JSFunction>::cast(proto); | |
| 912 FloodWithOneShotGeneric(function_proto); | |
| 913 } | |
| 914 | |
| 915 | |
| 916 void Debug::FloodWithOneShotGeneric(Handle<JSFunction> function, | |
| 917 Handle<Object> holder) { | |
| 918 if (function->shared()->bound()) { | |
| 919 FloodBoundFunctionWithOneShot(function); | |
| 920 } else if (function->shared()->is_default_constructor()) { | |
| 921 FloodDefaultConstructorWithOneShot(function); | |
| 922 } else { | |
| 923 Isolate* isolate = function->GetIsolate(); | |
| 924 // Don't allow step into functions in the native context. | |
| 925 if (function->shared()->code() == | |
| 926 isolate->builtins()->builtin(Builtins::kFunctionApply) || | |
| 927 function->shared()->code() == | |
| 928 isolate->builtins()->builtin(Builtins::kFunctionCall)) { | |
| 929 // Handle function.apply and function.call separately to flood the | |
| 930 // function to be called and not the code for Builtins::FunctionApply or | |
| 931 // Builtins::FunctionCall. The receiver of call/apply is the target | |
| 932 // function. | |
| 933 if (!holder.is_null() && holder->IsJSFunction()) { | |
| 934 Handle<JSFunction> js_function = Handle<JSFunction>::cast(holder); | |
| 935 FloodWithOneShotGeneric(js_function); | |
| 936 } | |
| 937 } else { | |
| 938 FloodWithOneShot(function); | |
| 939 } | |
| 940 } | |
| 941 } | |
| 942 | |
| 943 | |
| 944 void Debug::FloodHandlerWithOneShot() { | |
| 945 // Iterate through the JavaScript stack looking for handlers. | |
| 946 StackFrame::Id id = break_frame_id(); | |
| 947 if (id == StackFrame::NO_ID) { | |
| 948 // If there is no JavaScript stack don't do anything. | |
| 949 return; | |
| 950 } | |
| 951 for (JavaScriptFrameIterator it(isolate_, id); !it.done(); it.Advance()) { | |
| 952 JavaScriptFrame* frame = it.frame(); | |
| 953 int stack_slots = 0; // The computed stack slot count is not used. | |
| 954 if (frame->LookupExceptionHandlerInTable(&stack_slots, NULL) > 0) { | |
| 955 // Flood the function with the catch/finally block with break points. | |
| 956 FloodWithOneShot(Handle<JSFunction>(frame->function())); | |
| 957 return; | |
| 958 } | |
| 959 } | |
| 960 } | |
| 961 | |
| 962 | |
| 963 void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) { | |
| 964 if (type == BreakUncaughtException) { | |
| 965 break_on_uncaught_exception_ = enable; | |
| 966 } else { | |
| 967 break_on_exception_ = enable; | |
| 968 } | |
| 969 } | |
| 970 | |
| 971 | |
| 972 bool Debug::IsBreakOnException(ExceptionBreakType type) { | |
| 973 if (type == BreakUncaughtException) { | |
| 974 return break_on_uncaught_exception_; | |
| 975 } else { | |
| 976 return break_on_exception_; | |
| 977 } | |
| 978 } | |
| 979 | |
| 980 | |
| 981 FrameSummary GetFirstFrameSummary(JavaScriptFrame* frame) { | |
| 982 List<FrameSummary> frames(FLAG_max_inlining_levels + 1); | |
| 983 frame->Summarize(&frames); | |
| 984 return frames.first(); | |
| 985 } | |
| 986 | |
| 987 | |
| 988 void Debug::PrepareStep(StepAction step_action, | |
| 989 int step_count, | |
| 990 StackFrame::Id frame_id) { | |
| 991 HandleScope scope(isolate_); | |
| 992 | |
| 993 DCHECK(in_debug_scope()); | |
| 994 | |
| 995 // Remember this step action and count. | |
| 996 thread_local_.last_step_action_ = step_action; | |
| 997 if (step_action == StepOut) { | |
| 998 // For step out target frame will be found on the stack so there is no need | |
| 999 // to set step counter for it. It's expected to always be 0 for StepOut. | |
| 1000 thread_local_.step_count_ = 0; | |
| 1001 } else { | |
| 1002 thread_local_.step_count_ = step_count; | |
| 1003 } | |
| 1004 | |
| 1005 // Get the frame where the execution has stopped and skip the debug frame if | |
| 1006 // any. The debug frame will only be present if execution was stopped due to | |
| 1007 // hitting a break point. In other situations (e.g. unhandled exception) the | |
| 1008 // debug frame is not present. | |
| 1009 StackFrame::Id id = break_frame_id(); | |
| 1010 if (id == StackFrame::NO_ID) { | |
| 1011 // If there is no JavaScript stack don't do anything. | |
| 1012 return; | |
| 1013 } | |
| 1014 if (frame_id != StackFrame::NO_ID) { | |
| 1015 id = frame_id; | |
| 1016 } | |
| 1017 JavaScriptFrameIterator frames_it(isolate_, id); | |
| 1018 JavaScriptFrame* frame = frames_it.frame(); | |
| 1019 | |
| 1020 // First of all ensure there is one-shot break points in the top handler | |
| 1021 // if any. | |
| 1022 FloodHandlerWithOneShot(); | |
| 1023 | |
| 1024 // If the function on the top frame is unresolved perform step out. This will | |
| 1025 // be the case when calling unknown function and having the debugger stopped | |
| 1026 // in an unhandled exception. | |
| 1027 if (!frame->function()->IsJSFunction()) { | |
| 1028 // Step out: Find the calling JavaScript frame and flood it with | |
| 1029 // breakpoints. | |
| 1030 frames_it.Advance(); | |
| 1031 // Fill the function to return to with one-shot break points. | |
| 1032 JSFunction* function = frames_it.frame()->function(); | |
| 1033 FloodWithOneShot(Handle<JSFunction>(function)); | |
| 1034 return; | |
| 1035 } | |
| 1036 | |
| 1037 // Get the debug info (create it if it does not exist). | |
| 1038 FrameSummary summary = GetFirstFrameSummary(frame); | |
| 1039 Handle<JSFunction> function(summary.function()); | |
| 1040 Handle<SharedFunctionInfo> shared(function->shared()); | |
| 1041 if (!EnsureDebugInfo(shared, function)) { | |
| 1042 // Return if ensuring debug info failed. | |
| 1043 return; | |
| 1044 } | |
| 1045 | |
| 1046 Handle<DebugInfo> debug_info(shared->GetDebugInfo()); | |
| 1047 // Refresh frame summary if the code has been recompiled for debugging. | |
| 1048 if (shared->code() != *summary.code()) summary = GetFirstFrameSummary(frame); | |
| 1049 | |
| 1050 // PC points to the instruction after the current one, possibly a break | |
| 1051 // location as well. So the "- 1" to exclude it from the search. | |
| 1052 Address call_pc = summary.pc() - 1; | |
| 1053 BreakLocation location = | |
| 1054 BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc); | |
| 1055 | |
| 1056 // If this is the last break code target step out is the only possibility. | |
| 1057 if (location.IsReturn() || step_action == StepOut) { | |
| 1058 if (step_action == StepOut) { | |
| 1059 // Skip step_count frames starting with the current one. | |
| 1060 while (step_count-- > 0 && !frames_it.done()) { | |
| 1061 frames_it.Advance(); | |
| 1062 } | |
| 1063 } else { | |
| 1064 DCHECK(location.IsReturn()); | |
| 1065 frames_it.Advance(); | |
| 1066 } | |
| 1067 // Skip native and extension functions on the stack. | |
| 1068 while (!frames_it.done() && | |
| 1069 !frames_it.frame()->function()->IsSubjectToDebugging()) { | |
| 1070 frames_it.Advance(); | |
| 1071 } | |
| 1072 // Step out: If there is a JavaScript caller frame, we need to | |
| 1073 // flood it with breakpoints. | |
| 1074 if (!frames_it.done()) { | |
| 1075 // Fill the function to return to with one-shot break points. | |
| 1076 JSFunction* function = frames_it.frame()->function(); | |
| 1077 FloodWithOneShot(Handle<JSFunction>(function)); | |
| 1078 // Set target frame pointer. | |
| 1079 ActivateStepOut(frames_it.frame()); | |
| 1080 } | |
| 1081 return; | |
| 1082 } | |
| 1083 | |
| 1084 if (step_action != StepNext && step_action != StepMin) { | |
| 1085 // If there's restarter frame on top of the stack, just get the pointer | |
| 1086 // to function which is going to be restarted. | |
| 1087 if (thread_local_.restarter_frame_function_pointer_ != NULL) { | |
| 1088 Handle<JSFunction> restarted_function( | |
| 1089 JSFunction::cast(*thread_local_.restarter_frame_function_pointer_)); | |
| 1090 FloodWithOneShot(restarted_function); | |
| 1091 } else if (location.IsCall()) { | |
| 1092 // Find target function on the expression stack. | |
| 1093 // Expression stack looks like this (top to bottom): | |
| 1094 // argN | |
| 1095 // ... | |
| 1096 // arg0 | |
| 1097 // Receiver | |
| 1098 // Function to call | |
| 1099 int num_expressions_without_args = | |
| 1100 frame->ComputeExpressionsCount() - location.CallArgumentsCount(); | |
| 1101 DCHECK(num_expressions_without_args >= 2); | |
| 1102 Object* fun = frame->GetExpression(num_expressions_without_args - 2); | |
| 1103 | |
| 1104 // Flood the actual target of call/apply. | |
| 1105 if (fun->IsJSFunction()) { | |
| 1106 Isolate* isolate = JSFunction::cast(fun)->GetIsolate(); | |
| 1107 Code* apply = isolate->builtins()->builtin(Builtins::kFunctionApply); | |
| 1108 Code* call = isolate->builtins()->builtin(Builtins::kFunctionCall); | |
| 1109 // Find target function on the expression stack for expression like | |
| 1110 // Function.call.call...apply(...) | |
| 1111 int i = 1; | |
| 1112 while (fun->IsJSFunction()) { | |
| 1113 Code* code = JSFunction::cast(fun)->shared()->code(); | |
| 1114 if (code != apply && code != call) break; | |
| 1115 DCHECK(num_expressions_without_args >= i); | |
| 1116 fun = frame->GetExpression(num_expressions_without_args - i); | |
| 1117 i--; | |
| 1118 } | |
| 1119 } | |
| 1120 | |
| 1121 if (fun->IsJSFunction()) { | |
| 1122 Handle<JSFunction> js_function(JSFunction::cast(fun)); | |
| 1123 FloodWithOneShotGeneric(js_function); | |
| 1124 } | |
| 1125 } | |
| 1126 | |
| 1127 ActivateStepIn(frame); | |
| 1128 } | |
| 1129 | |
| 1130 // Fill the current function with one-shot break points even for step in on | |
| 1131 // a call target as the function called might be a native function for | |
| 1132 // which step in will not stop. It also prepares for stepping in | |
| 1133 // getters/setters. | |
| 1134 // If we are stepping into another frame, only fill calls and returns. | |
| 1135 FloodWithOneShot(function, step_action == StepFrame ? CALLS_AND_RETURNS | |
| 1136 : ALL_BREAK_LOCATIONS); | |
| 1137 | |
| 1138 // Remember source position and frame to handle step next. | |
| 1139 thread_local_.last_statement_position_ = | |
| 1140 debug_info->code()->SourceStatementPosition(summary.pc()); | |
| 1141 thread_local_.last_fp_ = frame->UnpaddedFP(); | |
| 1142 } | |
| 1143 | |
| 1144 | |
| 1145 // Check whether the current debug break should be reported to the debugger. It | |
| 1146 // is used to have step next and step in only report break back to the debugger | |
| 1147 // if on a different frame or in a different statement. In some situations | |
| 1148 // there will be several break points in the same statement when the code is | |
| 1149 // flooded with one-shot break points. This function helps to perform several | |
| 1150 // steps before reporting break back to the debugger. | |
| 1151 bool Debug::StepNextContinue(BreakLocation* break_location, | |
| 1152 JavaScriptFrame* frame) { | |
| 1153 // StepNext and StepOut shouldn't bring us deeper in code, so last frame | |
| 1154 // shouldn't be a parent of current frame. | |
| 1155 StepAction step_action = thread_local_.last_step_action_; | |
| 1156 | |
| 1157 if (step_action == StepNext || step_action == StepOut) { | |
| 1158 if (frame->fp() < thread_local_.last_fp_) return true; | |
| 1159 } | |
| 1160 | |
| 1161 // We stepped into a new frame if the frame pointer changed. | |
| 1162 if (step_action == StepFrame) { | |
| 1163 return frame->UnpaddedFP() == thread_local_.last_fp_; | |
| 1164 } | |
| 1165 | |
| 1166 // If the step last action was step next or step in make sure that a new | |
| 1167 // statement is hit. | |
| 1168 if (step_action == StepNext || step_action == StepIn) { | |
| 1169 // Never continue if returning from function. | |
| 1170 if (break_location->IsReturn()) return false; | |
| 1171 | |
| 1172 // Continue if we are still on the same frame and in the same statement. | |
| 1173 int current_statement_position = | |
| 1174 break_location->code()->SourceStatementPosition(frame->pc()); | |
| 1175 return thread_local_.last_fp_ == frame->UnpaddedFP() && | |
| 1176 thread_local_.last_statement_position_ == current_statement_position; | |
| 1177 } | |
| 1178 | |
| 1179 // No step next action - don't continue. | |
| 1180 return false; | |
| 1181 } | |
| 1182 | |
| 1183 | |
| 1184 // Check whether the code object at the specified address is a debug break code | |
| 1185 // object. | |
| 1186 bool Debug::IsDebugBreak(Address addr) { | |
| 1187 Code* code = Code::GetCodeFromTargetAddress(addr); | |
| 1188 return code->is_debug_stub(); | |
| 1189 } | |
| 1190 | |
| 1191 | |
| 1192 // Simple function for returning the source positions for active break points. | |
| 1193 Handle<Object> Debug::GetSourceBreakLocations( | |
| 1194 Handle<SharedFunctionInfo> shared, | |
| 1195 BreakPositionAlignment position_alignment) { | |
| 1196 Isolate* isolate = shared->GetIsolate(); | |
| 1197 Heap* heap = isolate->heap(); | |
| 1198 if (!shared->HasDebugInfo()) { | |
| 1199 return Handle<Object>(heap->undefined_value(), isolate); | |
| 1200 } | |
| 1201 Handle<DebugInfo> debug_info(shared->GetDebugInfo()); | |
| 1202 if (debug_info->GetBreakPointCount() == 0) { | |
| 1203 return Handle<Object>(heap->undefined_value(), isolate); | |
| 1204 } | |
| 1205 Handle<FixedArray> locations = | |
| 1206 isolate->factory()->NewFixedArray(debug_info->GetBreakPointCount()); | |
| 1207 int count = 0; | |
| 1208 for (int i = 0; i < debug_info->break_points()->length(); ++i) { | |
| 1209 if (!debug_info->break_points()->get(i)->IsUndefined()) { | |
| 1210 BreakPointInfo* break_point_info = | |
| 1211 BreakPointInfo::cast(debug_info->break_points()->get(i)); | |
| 1212 int break_points = break_point_info->GetBreakPointCount(); | |
| 1213 if (break_points == 0) continue; | |
| 1214 Smi* position = NULL; | |
| 1215 switch (position_alignment) { | |
| 1216 case STATEMENT_ALIGNED: | |
| 1217 position = break_point_info->statement_position(); | |
| 1218 break; | |
| 1219 case BREAK_POSITION_ALIGNED: | |
| 1220 position = break_point_info->source_position(); | |
| 1221 break; | |
| 1222 } | |
| 1223 for (int j = 0; j < break_points; ++j) locations->set(count++, position); | |
| 1224 } | |
| 1225 } | |
| 1226 return locations; | |
| 1227 } | |
| 1228 | |
| 1229 | |
| 1230 // Handle stepping into a function. | |
| 1231 void Debug::HandleStepIn(Handle<Object> function_obj, bool is_constructor) { | |
| 1232 // Flood getter/setter if we either step in or step to another frame. | |
| 1233 bool step_frame = thread_local_.last_step_action_ == StepFrame; | |
| 1234 if (!StepInActive() && !step_frame) return; | |
| 1235 if (!function_obj->IsJSFunction()) return; | |
| 1236 Handle<JSFunction> function = Handle<JSFunction>::cast(function_obj); | |
| 1237 Isolate* isolate = function->GetIsolate(); | |
| 1238 | |
| 1239 StackFrameIterator it(isolate); | |
| 1240 it.Advance(); | |
| 1241 // For constructor functions skip another frame. | |
| 1242 if (is_constructor) { | |
| 1243 DCHECK(it.frame()->is_construct()); | |
| 1244 it.Advance(); | |
| 1245 } | |
| 1246 Address fp = it.frame()->fp(); | |
| 1247 | |
| 1248 // Flood the function with one-shot break points if it is called from where | |
| 1249 // step into was requested, or when stepping into a new frame. | |
| 1250 if (fp == thread_local_.step_into_fp_ || step_frame) { | |
| 1251 FloodWithOneShotGeneric(function, Handle<Object>()); | |
| 1252 } | |
| 1253 } | |
| 1254 | |
| 1255 | |
| 1256 void Debug::ClearStepping() { | |
| 1257 // Clear the various stepping setup. | |
| 1258 ClearOneShot(); | |
| 1259 ClearStepIn(); | |
| 1260 ClearStepOut(); | |
| 1261 ClearStepNext(); | |
| 1262 | |
| 1263 // Clear multiple step counter. | |
| 1264 thread_local_.step_count_ = 0; | |
| 1265 } | |
| 1266 | |
| 1267 | |
| 1268 // Clears all the one-shot break points that are currently set. Normally this | |
| 1269 // function is called each time a break point is hit as one shot break points | |
| 1270 // are used to support stepping. | |
| 1271 void Debug::ClearOneShot() { | |
| 1272 // The current implementation just runs through all the breakpoints. When the | |
| 1273 // last break point for a function is removed that function is automatically | |
| 1274 // removed from the list. | |
| 1275 for (DebugInfoListNode* node = debug_info_list_; node != NULL; | |
| 1276 node = node->next()) { | |
| 1277 for (BreakLocation::Iterator it(node->debug_info(), ALL_BREAK_LOCATIONS); | |
| 1278 !it.Done(); it.Next()) { | |
| 1279 it.GetBreakLocation().ClearOneShot(); | |
| 1280 } | |
| 1281 } | |
| 1282 } | |
| 1283 | |
| 1284 | |
| 1285 void Debug::ActivateStepIn(StackFrame* frame) { | |
| 1286 DCHECK(!StepOutActive()); | |
| 1287 thread_local_.step_into_fp_ = frame->UnpaddedFP(); | |
| 1288 } | |
| 1289 | |
| 1290 | |
| 1291 void Debug::ClearStepIn() { | |
| 1292 thread_local_.step_into_fp_ = 0; | |
| 1293 } | |
| 1294 | |
| 1295 | |
| 1296 void Debug::ActivateStepOut(StackFrame* frame) { | |
| 1297 DCHECK(!StepInActive()); | |
| 1298 thread_local_.step_out_fp_ = frame->UnpaddedFP(); | |
| 1299 } | |
| 1300 | |
| 1301 | |
| 1302 void Debug::ClearStepOut() { | |
| 1303 thread_local_.step_out_fp_ = 0; | |
| 1304 } | |
| 1305 | |
| 1306 | |
| 1307 void Debug::ClearStepNext() { | |
| 1308 thread_local_.last_step_action_ = StepNone; | |
| 1309 thread_local_.last_statement_position_ = RelocInfo::kNoPosition; | |
| 1310 thread_local_.last_fp_ = 0; | |
| 1311 } | |
| 1312 | |
| 1313 | |
| 1314 bool MatchingCodeTargets(Code* target1, Code* target2) { | |
| 1315 if (target1 == target2) return true; | |
| 1316 if (target1->kind() != target2->kind()) return false; | |
| 1317 return target1->is_handler() || target1->is_inline_cache_stub(); | |
| 1318 } | |
| 1319 | |
| 1320 | |
| 1321 // Count the number of calls before the current frame PC to find the | |
| 1322 // corresponding PC in the newly recompiled code. | |
| 1323 static Address ComputeNewPcForRedirect(Code* new_code, Code* old_code, | |
| 1324 Address old_pc) { | |
| 1325 DCHECK_EQ(old_code->kind(), Code::FUNCTION); | |
| 1326 DCHECK_EQ(new_code->kind(), Code::FUNCTION); | |
| 1327 DCHECK(new_code->has_debug_break_slots()); | |
| 1328 static const int mask = RelocInfo::kCodeTargetMask; | |
| 1329 | |
| 1330 // Find the target of the current call. | |
| 1331 Code* target = NULL; | |
| 1332 intptr_t delta = 0; | |
| 1333 for (RelocIterator it(old_code, mask); !it.done(); it.next()) { | |
| 1334 RelocInfo* rinfo = it.rinfo(); | |
| 1335 Address current_pc = rinfo->pc(); | |
| 1336 // The frame PC is behind the call instruction by the call instruction size. | |
| 1337 if (current_pc > old_pc) break; | |
| 1338 delta = old_pc - current_pc; | |
| 1339 target = Code::GetCodeFromTargetAddress(rinfo->target_address()); | |
| 1340 } | |
| 1341 | |
| 1342 // Count the number of calls to the same target before the current call. | |
| 1343 int index = 0; | |
| 1344 for (RelocIterator it(old_code, mask); !it.done(); it.next()) { | |
| 1345 RelocInfo* rinfo = it.rinfo(); | |
| 1346 Address current_pc = rinfo->pc(); | |
| 1347 if (current_pc > old_pc) break; | |
| 1348 Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address()); | |
| 1349 if (MatchingCodeTargets(target, current)) index++; | |
| 1350 } | |
| 1351 | |
| 1352 DCHECK(index > 0); | |
| 1353 | |
| 1354 // Repeat the count on the new code to find corresponding call. | |
| 1355 for (RelocIterator it(new_code, mask); !it.done(); it.next()) { | |
| 1356 RelocInfo* rinfo = it.rinfo(); | |
| 1357 Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address()); | |
| 1358 if (MatchingCodeTargets(target, current)) index--; | |
| 1359 if (index == 0) return rinfo->pc() + delta; | |
| 1360 } | |
| 1361 | |
| 1362 UNREACHABLE(); | |
| 1363 return NULL; | |
| 1364 } | |
| 1365 | |
| 1366 | |
| 1367 // Count the number of continuations at which the current pc offset is at. | |
| 1368 static int ComputeContinuationIndexFromPcOffset(Code* code, int pc_offset) { | |
| 1369 DCHECK_EQ(code->kind(), Code::FUNCTION); | |
| 1370 Address pc = code->instruction_start() + pc_offset; | |
| 1371 int mask = RelocInfo::ModeMask(RelocInfo::GENERATOR_CONTINUATION); | |
| 1372 int index = 0; | |
| 1373 for (RelocIterator it(code, mask); !it.done(); it.next()) { | |
| 1374 index++; | |
| 1375 RelocInfo* rinfo = it.rinfo(); | |
| 1376 Address current_pc = rinfo->pc(); | |
| 1377 if (current_pc == pc) break; | |
| 1378 DCHECK(current_pc < pc); | |
| 1379 } | |
| 1380 return index; | |
| 1381 } | |
| 1382 | |
| 1383 | |
| 1384 // Find the pc offset for the given continuation index. | |
| 1385 static int ComputePcOffsetFromContinuationIndex(Code* code, int index) { | |
| 1386 DCHECK_EQ(code->kind(), Code::FUNCTION); | |
| 1387 DCHECK(code->has_debug_break_slots()); | |
| 1388 int mask = RelocInfo::ModeMask(RelocInfo::GENERATOR_CONTINUATION); | |
| 1389 RelocIterator it(code, mask); | |
| 1390 for (int i = 1; i < index; i++) it.next(); | |
| 1391 return static_cast<int>(it.rinfo()->pc() - code->instruction_start()); | |
| 1392 } | |
| 1393 | |
| 1394 | |
| 1395 class RedirectActiveFunctions : public ThreadVisitor { | |
| 1396 public: | |
| 1397 explicit RedirectActiveFunctions(SharedFunctionInfo* shared) | |
| 1398 : shared_(shared) { | |
| 1399 DCHECK(shared->HasDebugCode()); | |
| 1400 } | |
| 1401 | |
| 1402 void VisitThread(Isolate* isolate, ThreadLocalTop* top) { | |
| 1403 for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { | |
| 1404 JavaScriptFrame* frame = it.frame(); | |
| 1405 JSFunction* function = frame->function(); | |
| 1406 if (frame->is_optimized()) continue; | |
| 1407 if (!function->Inlines(shared_)) continue; | |
| 1408 | |
| 1409 Code* frame_code = frame->LookupCode(); | |
| 1410 DCHECK(frame_code->kind() == Code::FUNCTION); | |
| 1411 if (frame_code->has_debug_break_slots()) continue; | |
| 1412 | |
| 1413 Code* new_code = function->shared()->code(); | |
| 1414 Address old_pc = frame->pc(); | |
| 1415 Address new_pc = ComputeNewPcForRedirect(new_code, frame_code, old_pc); | |
| 1416 | |
| 1417 if (FLAG_trace_deopt) { | |
| 1418 PrintF("Replacing pc for debugging: %08" V8PRIxPTR " => %08" V8PRIxPTR | |
| 1419 "\n", | |
| 1420 reinterpret_cast<intptr_t>(old_pc), | |
| 1421 reinterpret_cast<intptr_t>(new_pc)); | |
| 1422 } | |
| 1423 | |
| 1424 if (FLAG_enable_embedded_constant_pool) { | |
| 1425 // Update constant pool pointer for new code. | |
| 1426 frame->set_constant_pool(new_code->constant_pool()); | |
| 1427 } | |
| 1428 | |
| 1429 // Patch the return address to return into the code with | |
| 1430 // debug break slots. | |
| 1431 frame->set_pc(new_pc); | |
| 1432 } | |
| 1433 } | |
| 1434 | |
| 1435 private: | |
| 1436 SharedFunctionInfo* shared_; | |
| 1437 DisallowHeapAllocation no_gc_; | |
| 1438 }; | |
| 1439 | |
| 1440 | |
| 1441 bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) { | |
| 1442 DCHECK(shared->is_compiled()); | |
| 1443 | |
| 1444 if (isolate_->concurrent_recompilation_enabled()) { | |
| 1445 isolate_->optimizing_compile_dispatcher()->Flush(); | |
| 1446 } | |
| 1447 | |
| 1448 List<Handle<JSFunction> > functions; | |
| 1449 List<Handle<JSGeneratorObject> > suspended_generators; | |
| 1450 | |
| 1451 if (!shared->optimized_code_map()->IsSmi()) { | |
| 1452 shared->ClearOptimizedCodeMap(); | |
| 1453 } | |
| 1454 | |
| 1455 // Make sure we abort incremental marking. | |
| 1456 isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask, | |
| 1457 "prepare for break points"); | |
| 1458 | |
| 1459 { | |
| 1460 HeapIterator iterator(isolate_->heap()); | |
| 1461 HeapObject* obj; | |
| 1462 bool include_generators = shared->is_generator(); | |
| 1463 | |
| 1464 while ((obj = iterator.next())) { | |
| 1465 if (obj->IsJSFunction()) { | |
| 1466 JSFunction* function = JSFunction::cast(obj); | |
| 1467 if (!function->Inlines(*shared)) continue; | |
| 1468 if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) { | |
| 1469 Deoptimizer::DeoptimizeFunction(function); | |
| 1470 } | |
| 1471 if (function->shared() == *shared) functions.Add(handle(function)); | |
| 1472 } else if (include_generators && obj->IsJSGeneratorObject()) { | |
| 1473 JSGeneratorObject* generator_obj = JSGeneratorObject::cast(obj); | |
| 1474 if (!generator_obj->is_suspended()) continue; | |
| 1475 JSFunction* function = generator_obj->function(); | |
| 1476 if (!function->Inlines(*shared)) continue; | |
| 1477 int pc_offset = generator_obj->continuation(); | |
| 1478 int index = | |
| 1479 ComputeContinuationIndexFromPcOffset(function->code(), pc_offset); | |
| 1480 generator_obj->set_continuation(index); | |
| 1481 suspended_generators.Add(handle(generator_obj)); | |
| 1482 } | |
| 1483 } | |
| 1484 } | |
| 1485 | |
| 1486 if (!shared->HasDebugCode()) { | |
| 1487 DCHECK(functions.length() > 0); | |
| 1488 if (!Compiler::CompileDebugCode(functions.first())) return false; | |
| 1489 } | |
| 1490 | |
| 1491 for (Handle<JSFunction> const function : functions) { | |
| 1492 function->ReplaceCode(shared->code()); | |
| 1493 } | |
| 1494 | |
| 1495 for (Handle<JSGeneratorObject> const generator_obj : suspended_generators) { | |
| 1496 int index = generator_obj->continuation(); | |
| 1497 int pc_offset = ComputePcOffsetFromContinuationIndex(shared->code(), index); | |
| 1498 generator_obj->set_continuation(pc_offset); | |
| 1499 } | |
| 1500 | |
| 1501 // Update PCs on the stack to point to recompiled code. | |
| 1502 RedirectActiveFunctions redirect_visitor(*shared); | |
| 1503 redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top()); | |
| 1504 isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor); | |
| 1505 | |
| 1506 return true; | |
| 1507 } | |
| 1508 | |
| 1509 | |
| 1510 class SharedFunctionInfoFinder { | |
| 1511 public: | |
| 1512 explicit SharedFunctionInfoFinder(int target_position) | |
| 1513 : current_candidate_(NULL), | |
| 1514 current_candidate_closure_(NULL), | |
| 1515 current_start_position_(RelocInfo::kNoPosition), | |
| 1516 target_position_(target_position) {} | |
| 1517 | |
| 1518 void NewCandidate(SharedFunctionInfo* shared, JSFunction* closure = NULL) { | |
| 1519 int start_position = shared->function_token_position(); | |
| 1520 if (start_position == RelocInfo::kNoPosition) { | |
| 1521 start_position = shared->start_position(); | |
| 1522 } | |
| 1523 | |
| 1524 if (start_position > target_position_) return; | |
| 1525 if (target_position_ > shared->end_position()) return; | |
| 1526 | |
| 1527 if (current_candidate_ != NULL) { | |
| 1528 if (current_start_position_ == start_position && | |
| 1529 shared->end_position() == current_candidate_->end_position()) { | |
| 1530 // If a top-level function contains only one function | |
| 1531 // declaration the source for the top-level and the function | |
| 1532 // is the same. In that case prefer the non top-level function. | |
| 1533 if (shared->is_toplevel()) return; | |
| 1534 } else if (start_position < current_start_position_ || | |
| 1535 current_candidate_->end_position() < shared->end_position()) { | |
| 1536 return; | |
| 1537 } | |
| 1538 } | |
| 1539 | |
| 1540 current_start_position_ = start_position; | |
| 1541 current_candidate_ = shared; | |
| 1542 current_candidate_closure_ = closure; | |
| 1543 } | |
| 1544 | |
| 1545 SharedFunctionInfo* Result() { return current_candidate_; } | |
| 1546 | |
| 1547 JSFunction* ResultClosure() { return current_candidate_closure_; } | |
| 1548 | |
| 1549 private: | |
| 1550 SharedFunctionInfo* current_candidate_; | |
| 1551 JSFunction* current_candidate_closure_; | |
| 1552 int current_start_position_; | |
| 1553 int target_position_; | |
| 1554 DisallowHeapAllocation no_gc_; | |
| 1555 }; | |
| 1556 | |
| 1557 | |
| 1558 // We need to find a SFI for a literal that may not yet have been compiled yet, | |
| 1559 // and there may not be a JSFunction referencing it. Find the SFI closest to | |
| 1560 // the given position, compile it to reveal possible inner SFIs and repeat. | |
| 1561 // While we are at this, also ensure code with debug break slots so that we do | |
| 1562 // not have to compile a SFI without JSFunction, which is paifu for those that | |
| 1563 // cannot be compiled without context (need to find outer compilable SFI etc.) | |
| 1564 Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script, | |
| 1565 int position) { | |
| 1566 while (true) { | |
| 1567 // Go through all shared function infos associated with this script to | |
| 1568 // find the inner most function containing this position. | |
| 1569 if (!script->shared_function_infos()->IsWeakFixedArray()) break; | |
| 1570 WeakFixedArray* array = | |
| 1571 WeakFixedArray::cast(script->shared_function_infos()); | |
| 1572 | |
| 1573 SharedFunctionInfo* shared; | |
| 1574 { | |
| 1575 SharedFunctionInfoFinder finder(position); | |
| 1576 for (int i = 0; i < array->Length(); i++) { | |
| 1577 Object* item = array->Get(i); | |
| 1578 if (!item->IsSharedFunctionInfo()) continue; | |
| 1579 finder.NewCandidate(SharedFunctionInfo::cast(item)); | |
| 1580 } | |
| 1581 shared = finder.Result(); | |
| 1582 if (shared == NULL) break; | |
| 1583 // We found it if it's already compiled and has debug code. | |
| 1584 if (shared->HasDebugCode()) return handle(shared); | |
| 1585 } | |
| 1586 // If not, compile to reveal inner functions, if possible. | |
| 1587 if (shared->allows_lazy_compilation_without_context()) { | |
| 1588 HandleScope scope(isolate_); | |
| 1589 if (!Compiler::CompileDebugCode(handle(shared))) break; | |
| 1590 continue; | |
| 1591 } | |
| 1592 | |
| 1593 // If not possible, comb the heap for the best suitable compile target. | |
| 1594 JSFunction* closure; | |
| 1595 { | |
| 1596 HeapIterator it(isolate_->heap()); | |
| 1597 SharedFunctionInfoFinder finder(position); | |
| 1598 while (HeapObject* object = it.next()) { | |
| 1599 JSFunction* candidate_closure = NULL; | |
| 1600 SharedFunctionInfo* candidate = NULL; | |
| 1601 if (object->IsJSFunction()) { | |
| 1602 candidate_closure = JSFunction::cast(object); | |
| 1603 candidate = candidate_closure->shared(); | |
| 1604 } else if (object->IsSharedFunctionInfo()) { | |
| 1605 candidate = SharedFunctionInfo::cast(object); | |
| 1606 if (!candidate->allows_lazy_compilation_without_context()) continue; | |
| 1607 } else { | |
| 1608 continue; | |
| 1609 } | |
| 1610 if (candidate->script() == *script) { | |
| 1611 finder.NewCandidate(candidate, candidate_closure); | |
| 1612 } | |
| 1613 } | |
| 1614 closure = finder.ResultClosure(); | |
| 1615 shared = finder.Result(); | |
| 1616 } | |
| 1617 HandleScope scope(isolate_); | |
| 1618 if (closure == NULL) { | |
| 1619 if (!Compiler::CompileDebugCode(handle(shared))) break; | |
| 1620 } else { | |
| 1621 if (!Compiler::CompileDebugCode(handle(closure))) break; | |
| 1622 } | |
| 1623 } | |
| 1624 return isolate_->factory()->undefined_value(); | |
| 1625 } | |
| 1626 | |
| 1627 | |
| 1628 // Ensures the debug information is present for shared. | |
| 1629 bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared, | |
| 1630 Handle<JSFunction> function) { | |
| 1631 if (!shared->IsSubjectToDebugging()) return false; | |
| 1632 | |
| 1633 // Return if we already have the debug info for shared. | |
| 1634 if (shared->HasDebugInfo()) return true; | |
| 1635 | |
| 1636 if (function.is_null()) { | |
| 1637 DCHECK(shared->HasDebugCode()); | |
| 1638 } else if (!Compiler::EnsureCompiled(function, CLEAR_EXCEPTION)) { | |
| 1639 return false; | |
| 1640 } | |
| 1641 | |
| 1642 if (!PrepareFunctionForBreakPoints(shared)) return false; | |
| 1643 | |
| 1644 // Make sure IC state is clean. This is so that we correctly flood | |
| 1645 // accessor pairs when stepping in. | |
| 1646 shared->code()->ClearInlineCaches(); | |
| 1647 shared->feedback_vector()->ClearICSlots(*shared); | |
| 1648 | |
| 1649 // Create the debug info object. | |
| 1650 DCHECK(shared->HasDebugCode()); | |
| 1651 Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared); | |
| 1652 | |
| 1653 // Add debug info to the list. | |
| 1654 DebugInfoListNode* node = new DebugInfoListNode(*debug_info); | |
| 1655 node->set_next(debug_info_list_); | |
| 1656 debug_info_list_ = node; | |
| 1657 | |
| 1658 return true; | |
| 1659 } | |
| 1660 | |
| 1661 | |
| 1662 void Debug::RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info) { | |
| 1663 HandleScope scope(isolate_); | |
| 1664 Handle<SharedFunctionInfo> shared(debug_info->shared()); | |
| 1665 | |
| 1666 DCHECK_NOT_NULL(debug_info_list_); | |
| 1667 // Run through the debug info objects to find this one and remove it. | |
| 1668 DebugInfoListNode* prev = NULL; | |
| 1669 DebugInfoListNode* current = debug_info_list_; | |
| 1670 while (current != NULL) { | |
| 1671 if (current->debug_info().is_identical_to(debug_info)) { | |
| 1672 // Unlink from list. If prev is NULL we are looking at the first element. | |
| 1673 if (prev == NULL) { | |
| 1674 debug_info_list_ = current->next(); | |
| 1675 } else { | |
| 1676 prev->set_next(current->next()); | |
| 1677 } | |
| 1678 delete current; | |
| 1679 shared->set_debug_info(isolate_->heap()->undefined_value()); | |
| 1680 return; | |
| 1681 } | |
| 1682 // Move to next in list. | |
| 1683 prev = current; | |
| 1684 current = current->next(); | |
| 1685 } | |
| 1686 | |
| 1687 UNREACHABLE(); | |
| 1688 } | |
| 1689 | |
| 1690 | |
| 1691 void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) { | |
| 1692 after_break_target_ = NULL; | |
| 1693 | |
| 1694 if (LiveEdit::SetAfterBreakTarget(this)) return; // LiveEdit did the job. | |
| 1695 | |
| 1696 // Continue just after the slot. | |
| 1697 after_break_target_ = frame->pc(); | |
| 1698 } | |
| 1699 | |
| 1700 | |
| 1701 bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) { | |
| 1702 HandleScope scope(isolate_); | |
| 1703 | |
| 1704 // Get the executing function in which the debug break occurred. | |
| 1705 Handle<JSFunction> function(JSFunction::cast(frame->function())); | |
| 1706 Handle<SharedFunctionInfo> shared(function->shared()); | |
| 1707 | |
| 1708 // With no debug info there are no break points, so we can't be at a return. | |
| 1709 if (!shared->HasDebugInfo()) return false; | |
| 1710 Handle<DebugInfo> debug_info(shared->GetDebugInfo()); | |
| 1711 Handle<Code> code(debug_info->code()); | |
| 1712 #ifdef DEBUG | |
| 1713 // Get the code which is actually executing. | |
| 1714 Handle<Code> frame_code(frame->LookupCode()); | |
| 1715 DCHECK(frame_code.is_identical_to(code)); | |
| 1716 #endif | |
| 1717 | |
| 1718 // Find the reloc info matching the start of the debug break slot. | |
| 1719 Address slot_pc = frame->pc() - Assembler::kDebugBreakSlotLength; | |
| 1720 int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN); | |
| 1721 for (RelocIterator it(*code, mask); !it.done(); it.next()) { | |
| 1722 if (it.rinfo()->pc() == slot_pc) return true; | |
| 1723 } | |
| 1724 return false; | |
| 1725 } | |
| 1726 | |
| 1727 | |
| 1728 void Debug::FramesHaveBeenDropped(StackFrame::Id new_break_frame_id, | |
| 1729 LiveEdit::FrameDropMode mode, | |
| 1730 Object** restarter_frame_function_pointer) { | |
| 1731 if (mode != LiveEdit::CURRENTLY_SET_MODE) { | |
| 1732 thread_local_.frame_drop_mode_ = mode; | |
| 1733 } | |
| 1734 thread_local_.break_frame_id_ = new_break_frame_id; | |
| 1735 thread_local_.restarter_frame_function_pointer_ = | |
| 1736 restarter_frame_function_pointer; | |
| 1737 } | |
| 1738 | |
| 1739 | |
| 1740 bool Debug::IsDebugGlobal(GlobalObject* global) { | |
| 1741 return is_loaded() && global == debug_context()->global_object(); | |
| 1742 } | |
| 1743 | |
| 1744 | |
| 1745 void Debug::ClearMirrorCache() { | |
| 1746 PostponeInterruptsScope postpone(isolate_); | |
| 1747 HandleScope scope(isolate_); | |
| 1748 AssertDebugContext(); | |
| 1749 Factory* factory = isolate_->factory(); | |
| 1750 Handle<GlobalObject> global(isolate_->global_object()); | |
| 1751 JSObject::SetProperty(global, | |
| 1752 factory->NewStringFromAsciiChecked("next_handle_"), | |
| 1753 handle(Smi::FromInt(0), isolate_), SLOPPY).Check(); | |
| 1754 JSObject::SetProperty(global, | |
| 1755 factory->NewStringFromAsciiChecked("mirror_cache_"), | |
| 1756 factory->NewJSArray(0, FAST_ELEMENTS), SLOPPY).Check(); | |
| 1757 } | |
| 1758 | |
| 1759 | |
| 1760 Handle<FixedArray> Debug::GetLoadedScripts() { | |
| 1761 // Create and fill the script cache when the loaded scripts is requested for | |
| 1762 // the first time. | |
| 1763 if (script_cache_ == NULL) script_cache_ = new ScriptCache(isolate_); | |
| 1764 | |
| 1765 // Perform GC to get unreferenced scripts evicted from the cache before | |
| 1766 // returning the content. | |
| 1767 isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags, | |
| 1768 "Debug::GetLoadedScripts"); | |
| 1769 | |
| 1770 // Get the scripts from the cache. | |
| 1771 return script_cache_->GetScripts(); | |
| 1772 } | |
| 1773 | |
| 1774 | |
| 1775 void Debug::GetStepinPositions(JavaScriptFrame* frame, StackFrame::Id frame_id, | |
| 1776 List<int>* results_out) { | |
| 1777 FrameSummary summary = GetFirstFrameSummary(frame); | |
| 1778 | |
| 1779 Handle<JSFunction> fun = Handle<JSFunction>(summary.function()); | |
| 1780 Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>(fun->shared()); | |
| 1781 | |
| 1782 if (!EnsureDebugInfo(shared, fun)) return; | |
| 1783 | |
| 1784 Handle<DebugInfo> debug_info(shared->GetDebugInfo()); | |
| 1785 // Refresh frame summary if the code has been recompiled for debugging. | |
| 1786 if (shared->code() != *summary.code()) summary = GetFirstFrameSummary(frame); | |
| 1787 | |
| 1788 // Find range of break points starting from the break point where execution | |
| 1789 // has stopped. | |
| 1790 Address call_pc = summary.pc() - 1; | |
| 1791 List<BreakLocation> locations; | |
| 1792 BreakLocation::FromAddressSameStatement(debug_info, ALL_BREAK_LOCATIONS, | |
| 1793 call_pc, &locations); | |
| 1794 | |
| 1795 for (BreakLocation location : locations) { | |
| 1796 if (location.pc() <= summary.pc()) { | |
| 1797 // The break point is near our pc. Could be a step-in possibility, | |
| 1798 // that is currently taken by active debugger call. | |
| 1799 if (break_frame_id() == StackFrame::NO_ID) { | |
| 1800 continue; // We are not stepping. | |
| 1801 } else { | |
| 1802 JavaScriptFrameIterator frame_it(isolate_, break_frame_id()); | |
| 1803 // If our frame is a top frame and we are stepping, we can do step-in | |
| 1804 // at this place. | |
| 1805 if (frame_it.frame()->id() != frame_id) continue; | |
| 1806 } | |
| 1807 } | |
| 1808 if (location.IsStepInLocation()) results_out->Add(location.position()); | |
| 1809 } | |
| 1810 } | |
| 1811 | |
| 1812 | |
| 1813 void Debug::RecordEvalCaller(Handle<Script> script) { | |
| 1814 script->set_compilation_type(Script::COMPILATION_TYPE_EVAL); | |
| 1815 // For eval scripts add information on the function from which eval was | |
| 1816 // called. | |
| 1817 StackTraceFrameIterator it(script->GetIsolate()); | |
| 1818 if (!it.done()) { | |
| 1819 script->set_eval_from_shared(it.frame()->function()->shared()); | |
| 1820 Code* code = it.frame()->LookupCode(); | |
| 1821 int offset = static_cast<int>( | |
| 1822 it.frame()->pc() - code->instruction_start()); | |
| 1823 script->set_eval_from_instructions_offset(Smi::FromInt(offset)); | |
| 1824 } | |
| 1825 } | |
| 1826 | |
| 1827 | |
| 1828 MaybeHandle<Object> Debug::MakeJSObject(const char* constructor_name, | |
| 1829 int argc, | |
| 1830 Handle<Object> argv[]) { | |
| 1831 AssertDebugContext(); | |
| 1832 // Create the execution state object. | |
| 1833 Handle<GlobalObject> global(isolate_->global_object()); | |
| 1834 Handle<Object> constructor = Object::GetProperty( | |
| 1835 isolate_, global, constructor_name).ToHandleChecked(); | |
| 1836 DCHECK(constructor->IsJSFunction()); | |
| 1837 if (!constructor->IsJSFunction()) return MaybeHandle<Object>(); | |
| 1838 // We do not handle interrupts here. In particular, termination interrupts. | |
| 1839 PostponeInterruptsScope no_interrupts(isolate_); | |
| 1840 return Execution::TryCall(Handle<JSFunction>::cast(constructor), | |
| 1841 handle(debug_context()->global_proxy()), | |
| 1842 argc, | |
| 1843 argv); | |
| 1844 } | |
| 1845 | |
| 1846 | |
| 1847 MaybeHandle<Object> Debug::MakeExecutionState() { | |
| 1848 // Create the execution state object. | |
| 1849 Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()) }; | |
| 1850 return MakeJSObject("MakeExecutionState", arraysize(argv), argv); | |
| 1851 } | |
| 1852 | |
| 1853 | |
| 1854 MaybeHandle<Object> Debug::MakeBreakEvent(Handle<Object> break_points_hit) { | |
| 1855 // Create the new break event object. | |
| 1856 Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()), | |
| 1857 break_points_hit }; | |
| 1858 return MakeJSObject("MakeBreakEvent", arraysize(argv), argv); | |
| 1859 } | |
| 1860 | |
| 1861 | |
| 1862 MaybeHandle<Object> Debug::MakeExceptionEvent(Handle<Object> exception, | |
| 1863 bool uncaught, | |
| 1864 Handle<Object> promise) { | |
| 1865 // Create the new exception event object. | |
| 1866 Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()), | |
| 1867 exception, | |
| 1868 isolate_->factory()->ToBoolean(uncaught), | |
| 1869 promise }; | |
| 1870 return MakeJSObject("MakeExceptionEvent", arraysize(argv), argv); | |
| 1871 } | |
| 1872 | |
| 1873 | |
| 1874 MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script, | |
| 1875 v8::DebugEvent type) { | |
| 1876 // Create the compile event object. | |
| 1877 Handle<Object> script_wrapper = Script::GetWrapper(script); | |
| 1878 Handle<Object> argv[] = { script_wrapper, | |
| 1879 isolate_->factory()->NewNumberFromInt(type) }; | |
| 1880 return MakeJSObject("MakeCompileEvent", arraysize(argv), argv); | |
| 1881 } | |
| 1882 | |
| 1883 | |
| 1884 MaybeHandle<Object> Debug::MakePromiseEvent(Handle<JSObject> event_data) { | |
| 1885 // Create the promise event object. | |
| 1886 Handle<Object> argv[] = { event_data }; | |
| 1887 return MakeJSObject("MakePromiseEvent", arraysize(argv), argv); | |
| 1888 } | |
| 1889 | |
| 1890 | |
| 1891 MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<JSObject> task_event) { | |
| 1892 // Create the async task event object. | |
| 1893 Handle<Object> argv[] = { task_event }; | |
| 1894 return MakeJSObject("MakeAsyncTaskEvent", arraysize(argv), argv); | |
| 1895 } | |
| 1896 | |
| 1897 | |
| 1898 void Debug::OnThrow(Handle<Object> exception) { | |
| 1899 if (in_debug_scope() || ignore_events()) return; | |
| 1900 // Temporarily clear any scheduled_exception to allow evaluating | |
| 1901 // JavaScript from the debug event handler. | |
| 1902 HandleScope scope(isolate_); | |
| 1903 Handle<Object> scheduled_exception; | |
| 1904 if (isolate_->has_scheduled_exception()) { | |
| 1905 scheduled_exception = handle(isolate_->scheduled_exception(), isolate_); | |
| 1906 isolate_->clear_scheduled_exception(); | |
| 1907 } | |
| 1908 OnException(exception, isolate_->GetPromiseOnStackOnThrow()); | |
| 1909 if (!scheduled_exception.is_null()) { | |
| 1910 isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception; | |
| 1911 } | |
| 1912 } | |
| 1913 | |
| 1914 | |
| 1915 void Debug::OnPromiseReject(Handle<JSObject> promise, Handle<Object> value) { | |
| 1916 if (in_debug_scope() || ignore_events()) return; | |
| 1917 HandleScope scope(isolate_); | |
| 1918 // Check whether the promise has been marked as having triggered a message. | |
| 1919 Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol(); | |
| 1920 if (JSReceiver::GetDataProperty(promise, key)->IsUndefined()) { | |
| 1921 OnException(value, promise); | |
| 1922 } | |
| 1923 } | |
| 1924 | |
| 1925 | |
| 1926 MaybeHandle<Object> Debug::PromiseHasUserDefinedRejectHandler( | |
| 1927 Handle<JSObject> promise) { | |
| 1928 Handle<JSFunction> fun = Handle<JSFunction>::cast( | |
| 1929 JSReceiver::GetDataProperty(isolate_->js_builtins_object(), | |
| 1930 isolate_->factory()->NewStringFromStaticChars( | |
| 1931 "$promiseHasUserDefinedRejectHandler"))); | |
| 1932 return Execution::Call(isolate_, fun, promise, 0, NULL); | |
| 1933 } | |
| 1934 | |
| 1935 | |
| 1936 void Debug::OnException(Handle<Object> exception, Handle<Object> promise) { | |
| 1937 // In our prediction, try-finally is not considered to catch. | |
| 1938 Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher(); | |
| 1939 bool uncaught = (catch_type == Isolate::NOT_CAUGHT); | |
| 1940 if (promise->IsJSObject()) { | |
| 1941 Handle<JSObject> jspromise = Handle<JSObject>::cast(promise); | |
| 1942 // Mark the promise as already having triggered a message. | |
| 1943 Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol(); | |
| 1944 JSObject::SetProperty(jspromise, key, key, STRICT).Assert(); | |
| 1945 // Check whether the promise reject is considered an uncaught exception. | |
| 1946 Handle<Object> has_reject_handler; | |
| 1947 ASSIGN_RETURN_ON_EXCEPTION_VALUE( | |
| 1948 isolate_, has_reject_handler, | |
| 1949 PromiseHasUserDefinedRejectHandler(jspromise), /* void */); | |
| 1950 uncaught = has_reject_handler->IsFalse(); | |
| 1951 } | |
| 1952 // Bail out if exception breaks are not active | |
| 1953 if (uncaught) { | |
| 1954 // Uncaught exceptions are reported by either flags. | |
| 1955 if (!(break_on_uncaught_exception_ || break_on_exception_)) return; | |
| 1956 } else { | |
| 1957 // Caught exceptions are reported is activated. | |
| 1958 if (!break_on_exception_) return; | |
| 1959 } | |
| 1960 | |
| 1961 DebugScope debug_scope(this); | |
| 1962 if (debug_scope.failed()) return; | |
| 1963 | |
| 1964 // Clear all current stepping setup. | |
| 1965 ClearStepping(); | |
| 1966 | |
| 1967 // Create the event data object. | |
| 1968 Handle<Object> event_data; | |
| 1969 // Bail out and don't call debugger if exception. | |
| 1970 if (!MakeExceptionEvent( | |
| 1971 exception, uncaught, promise).ToHandle(&event_data)) { | |
| 1972 return; | |
| 1973 } | |
| 1974 | |
| 1975 // Process debug event. | |
| 1976 ProcessDebugEvent(v8::Exception, Handle<JSObject>::cast(event_data), false); | |
| 1977 // Return to continue execution from where the exception was thrown. | |
| 1978 } | |
| 1979 | |
| 1980 | |
| 1981 void Debug::OnCompileError(Handle<Script> script) { | |
| 1982 if (ignore_events()) return; | |
| 1983 | |
| 1984 if (in_debug_scope()) { | |
| 1985 ProcessCompileEventInDebugScope(v8::CompileError, script); | |
| 1986 return; | |
| 1987 } | |
| 1988 | |
| 1989 HandleScope scope(isolate_); | |
| 1990 DebugScope debug_scope(this); | |
| 1991 if (debug_scope.failed()) return; | |
| 1992 | |
| 1993 // Create the compile state object. | |
| 1994 Handle<Object> event_data; | |
| 1995 // Bail out and don't call debugger if exception. | |
| 1996 if (!MakeCompileEvent(script, v8::CompileError).ToHandle(&event_data)) return; | |
| 1997 | |
| 1998 // Process debug event. | |
| 1999 ProcessDebugEvent(v8::CompileError, Handle<JSObject>::cast(event_data), true); | |
| 2000 } | |
| 2001 | |
| 2002 | |
| 2003 void Debug::OnDebugBreak(Handle<Object> break_points_hit, | |
| 2004 bool auto_continue) { | |
| 2005 // The caller provided for DebugScope. | |
| 2006 AssertDebugContext(); | |
| 2007 // Bail out if there is no listener for this event | |
| 2008 if (ignore_events()) return; | |
| 2009 | |
| 2010 HandleScope scope(isolate_); | |
| 2011 // Create the event data object. | |
| 2012 Handle<Object> event_data; | |
| 2013 // Bail out and don't call debugger if exception. | |
| 2014 if (!MakeBreakEvent(break_points_hit).ToHandle(&event_data)) return; | |
| 2015 | |
| 2016 // Process debug event. | |
| 2017 ProcessDebugEvent(v8::Break, | |
| 2018 Handle<JSObject>::cast(event_data), | |
| 2019 auto_continue); | |
| 2020 } | |
| 2021 | |
| 2022 | |
| 2023 void Debug::OnBeforeCompile(Handle<Script> script) { | |
| 2024 if (in_debug_scope() || ignore_events()) return; | |
| 2025 | |
| 2026 HandleScope scope(isolate_); | |
| 2027 DebugScope debug_scope(this); | |
| 2028 if (debug_scope.failed()) return; | |
| 2029 | |
| 2030 // Create the event data object. | |
| 2031 Handle<Object> event_data; | |
| 2032 // Bail out and don't call debugger if exception. | |
| 2033 if (!MakeCompileEvent(script, v8::BeforeCompile).ToHandle(&event_data)) | |
| 2034 return; | |
| 2035 | |
| 2036 // Process debug event. | |
| 2037 ProcessDebugEvent(v8::BeforeCompile, | |
| 2038 Handle<JSObject>::cast(event_data), | |
| 2039 true); | |
| 2040 } | |
| 2041 | |
| 2042 | |
| 2043 // Handle debugger actions when a new script is compiled. | |
| 2044 void Debug::OnAfterCompile(Handle<Script> script) { | |
| 2045 // Add the newly compiled script to the script cache. | |
| 2046 if (script_cache_ != NULL) script_cache_->Add(script); | |
| 2047 | |
| 2048 if (ignore_events()) return; | |
| 2049 | |
| 2050 if (in_debug_scope()) { | |
| 2051 ProcessCompileEventInDebugScope(v8::AfterCompile, script); | |
| 2052 return; | |
| 2053 } | |
| 2054 | |
| 2055 HandleScope scope(isolate_); | |
| 2056 DebugScope debug_scope(this); | |
| 2057 if (debug_scope.failed()) return; | |
| 2058 | |
| 2059 // If debugging there might be script break points registered for this | |
| 2060 // script. Make sure that these break points are set. | |
| 2061 | |
| 2062 // Get the function UpdateScriptBreakPoints (defined in debug-debugger.js). | |
| 2063 Handle<String> update_script_break_points_string = | |
| 2064 isolate_->factory()->InternalizeOneByteString( | |
| 2065 STATIC_CHAR_VECTOR("UpdateScriptBreakPoints")); | |
| 2066 Handle<GlobalObject> debug_global(debug_context()->global_object()); | |
| 2067 Handle<Object> update_script_break_points = | |
| 2068 Object::GetProperty( | |
| 2069 debug_global, update_script_break_points_string).ToHandleChecked(); | |
| 2070 if (!update_script_break_points->IsJSFunction()) { | |
| 2071 return; | |
| 2072 } | |
| 2073 DCHECK(update_script_break_points->IsJSFunction()); | |
| 2074 | |
| 2075 // Wrap the script object in a proper JS object before passing it | |
| 2076 // to JavaScript. | |
| 2077 Handle<Object> wrapper = Script::GetWrapper(script); | |
| 2078 | |
| 2079 // Call UpdateScriptBreakPoints expect no exceptions. | |
| 2080 Handle<Object> argv[] = { wrapper }; | |
| 2081 if (Execution::TryCall(Handle<JSFunction>::cast(update_script_break_points), | |
| 2082 isolate_->js_builtins_object(), | |
| 2083 arraysize(argv), | |
| 2084 argv).is_null()) { | |
| 2085 return; | |
| 2086 } | |
| 2087 | |
| 2088 // Create the compile state object. | |
| 2089 Handle<Object> event_data; | |
| 2090 // Bail out and don't call debugger if exception. | |
| 2091 if (!MakeCompileEvent(script, v8::AfterCompile).ToHandle(&event_data)) return; | |
| 2092 | |
| 2093 // Process debug event. | |
| 2094 ProcessDebugEvent(v8::AfterCompile, Handle<JSObject>::cast(event_data), true); | |
| 2095 } | |
| 2096 | |
| 2097 | |
| 2098 void Debug::OnPromiseEvent(Handle<JSObject> data) { | |
| 2099 if (in_debug_scope() || ignore_events()) return; | |
| 2100 | |
| 2101 HandleScope scope(isolate_); | |
| 2102 DebugScope debug_scope(this); | |
| 2103 if (debug_scope.failed()) return; | |
| 2104 | |
| 2105 // Create the script collected state object. | |
| 2106 Handle<Object> event_data; | |
| 2107 // Bail out and don't call debugger if exception. | |
| 2108 if (!MakePromiseEvent(data).ToHandle(&event_data)) return; | |
| 2109 | |
| 2110 // Process debug event. | |
| 2111 ProcessDebugEvent(v8::PromiseEvent, | |
| 2112 Handle<JSObject>::cast(event_data), | |
| 2113 true); | |
| 2114 } | |
| 2115 | |
| 2116 | |
| 2117 void Debug::OnAsyncTaskEvent(Handle<JSObject> data) { | |
| 2118 if (in_debug_scope() || ignore_events()) return; | |
| 2119 | |
| 2120 HandleScope scope(isolate_); | |
| 2121 DebugScope debug_scope(this); | |
| 2122 if (debug_scope.failed()) return; | |
| 2123 | |
| 2124 // Create the script collected state object. | |
| 2125 Handle<Object> event_data; | |
| 2126 // Bail out and don't call debugger if exception. | |
| 2127 if (!MakeAsyncTaskEvent(data).ToHandle(&event_data)) return; | |
| 2128 | |
| 2129 // Process debug event. | |
| 2130 ProcessDebugEvent(v8::AsyncTaskEvent, | |
| 2131 Handle<JSObject>::cast(event_data), | |
| 2132 true); | |
| 2133 } | |
| 2134 | |
| 2135 | |
| 2136 void Debug::ProcessDebugEvent(v8::DebugEvent event, | |
| 2137 Handle<JSObject> event_data, | |
| 2138 bool auto_continue) { | |
| 2139 HandleScope scope(isolate_); | |
| 2140 | |
| 2141 // Create the execution state. | |
| 2142 Handle<Object> exec_state; | |
| 2143 // Bail out and don't call debugger if exception. | |
| 2144 if (!MakeExecutionState().ToHandle(&exec_state)) return; | |
| 2145 | |
| 2146 // First notify the message handler if any. | |
| 2147 if (message_handler_ != NULL) { | |
| 2148 NotifyMessageHandler(event, | |
| 2149 Handle<JSObject>::cast(exec_state), | |
| 2150 event_data, | |
| 2151 auto_continue); | |
| 2152 } | |
| 2153 // Notify registered debug event listener. This can be either a C or | |
| 2154 // a JavaScript function. Don't call event listener for v8::Break | |
| 2155 // here, if it's only a debug command -- they will be processed later. | |
| 2156 if ((event != v8::Break || !auto_continue) && !event_listener_.is_null()) { | |
| 2157 CallEventCallback(event, exec_state, event_data, NULL); | |
| 2158 } | |
| 2159 } | |
| 2160 | |
| 2161 | |
| 2162 void Debug::CallEventCallback(v8::DebugEvent event, | |
| 2163 Handle<Object> exec_state, | |
| 2164 Handle<Object> event_data, | |
| 2165 v8::Debug::ClientData* client_data) { | |
| 2166 bool previous = in_debug_event_listener_; | |
| 2167 in_debug_event_listener_ = true; | |
| 2168 if (event_listener_->IsForeign()) { | |
| 2169 // Invoke the C debug event listener. | |
| 2170 v8::Debug::EventCallback callback = | |
| 2171 FUNCTION_CAST<v8::Debug::EventCallback>( | |
| 2172 Handle<Foreign>::cast(event_listener_)->foreign_address()); | |
| 2173 EventDetailsImpl event_details(event, | |
| 2174 Handle<JSObject>::cast(exec_state), | |
| 2175 Handle<JSObject>::cast(event_data), | |
| 2176 event_listener_data_, | |
| 2177 client_data); | |
| 2178 callback(event_details); | |
| 2179 DCHECK(!isolate_->has_scheduled_exception()); | |
| 2180 } else { | |
| 2181 // Invoke the JavaScript debug event listener. | |
| 2182 DCHECK(event_listener_->IsJSFunction()); | |
| 2183 Handle<Object> argv[] = { Handle<Object>(Smi::FromInt(event), isolate_), | |
| 2184 exec_state, | |
| 2185 event_data, | |
| 2186 event_listener_data_ }; | |
| 2187 Handle<JSReceiver> global(isolate_->global_proxy()); | |
| 2188 Execution::TryCall(Handle<JSFunction>::cast(event_listener_), | |
| 2189 global, arraysize(argv), argv); | |
| 2190 } | |
| 2191 in_debug_event_listener_ = previous; | |
| 2192 } | |
| 2193 | |
| 2194 | |
| 2195 void Debug::ProcessCompileEventInDebugScope(v8::DebugEvent event, | |
| 2196 Handle<Script> script) { | |
| 2197 if (event_listener_.is_null()) return; | |
| 2198 | |
| 2199 SuppressDebug while_processing(this); | |
| 2200 DebugScope debug_scope(this); | |
| 2201 if (debug_scope.failed()) return; | |
| 2202 | |
| 2203 Handle<Object> event_data; | |
| 2204 // Bail out and don't call debugger if exception. | |
| 2205 if (!MakeCompileEvent(script, event).ToHandle(&event_data)) return; | |
| 2206 | |
| 2207 // Create the execution state. | |
| 2208 Handle<Object> exec_state; | |
| 2209 // Bail out and don't call debugger if exception. | |
| 2210 if (!MakeExecutionState().ToHandle(&exec_state)) return; | |
| 2211 | |
| 2212 CallEventCallback(event, exec_state, event_data, NULL); | |
| 2213 } | |
| 2214 | |
| 2215 | |
| 2216 Handle<Context> Debug::GetDebugContext() { | |
| 2217 if (!is_loaded()) return Handle<Context>(); | |
| 2218 DebugScope debug_scope(this); | |
| 2219 if (debug_scope.failed()) return Handle<Context>(); | |
| 2220 // The global handle may be destroyed soon after. Return it reboxed. | |
| 2221 return handle(*debug_context(), isolate_); | |
| 2222 } | |
| 2223 | |
| 2224 | |
| 2225 void Debug::NotifyMessageHandler(v8::DebugEvent event, | |
| 2226 Handle<JSObject> exec_state, | |
| 2227 Handle<JSObject> event_data, | |
| 2228 bool auto_continue) { | |
| 2229 // Prevent other interrupts from triggering, for example API callbacks, | |
| 2230 // while dispatching message handler callbacks. | |
| 2231 PostponeInterruptsScope no_interrupts(isolate_); | |
| 2232 DCHECK(is_active_); | |
| 2233 HandleScope scope(isolate_); | |
| 2234 // Process the individual events. | |
| 2235 bool sendEventMessage = false; | |
| 2236 switch (event) { | |
| 2237 case v8::Break: | |
| 2238 sendEventMessage = !auto_continue; | |
| 2239 break; | |
| 2240 case v8::NewFunction: | |
| 2241 case v8::BeforeCompile: | |
| 2242 case v8::CompileError: | |
| 2243 case v8::PromiseEvent: | |
| 2244 case v8::AsyncTaskEvent: | |
| 2245 break; | |
| 2246 case v8::Exception: | |
| 2247 case v8::AfterCompile: | |
| 2248 sendEventMessage = true; | |
| 2249 break; | |
| 2250 } | |
| 2251 | |
| 2252 // The debug command interrupt flag might have been set when the command was | |
| 2253 // added. It should be enough to clear the flag only once while we are in the | |
| 2254 // debugger. | |
| 2255 DCHECK(in_debug_scope()); | |
| 2256 isolate_->stack_guard()->ClearDebugCommand(); | |
| 2257 | |
| 2258 // Notify the debugger that a debug event has occurred unless auto continue is | |
| 2259 // active in which case no event is send. | |
| 2260 if (sendEventMessage) { | |
| 2261 MessageImpl message = MessageImpl::NewEvent( | |
| 2262 event, | |
| 2263 auto_continue, | |
| 2264 Handle<JSObject>::cast(exec_state), | |
| 2265 Handle<JSObject>::cast(event_data)); | |
| 2266 InvokeMessageHandler(message); | |
| 2267 } | |
| 2268 | |
| 2269 // If auto continue don't make the event cause a break, but process messages | |
| 2270 // in the queue if any. For script collected events don't even process | |
| 2271 // messages in the queue as the execution state might not be what is expected | |
| 2272 // by the client. | |
| 2273 if (auto_continue && !has_commands()) return; | |
| 2274 | |
| 2275 // DebugCommandProcessor goes here. | |
| 2276 bool running = auto_continue; | |
| 2277 | |
| 2278 Handle<Object> cmd_processor_ctor = Object::GetProperty( | |
| 2279 isolate_, exec_state, "debugCommandProcessor").ToHandleChecked(); | |
| 2280 Handle<Object> ctor_args[] = { isolate_->factory()->ToBoolean(running) }; | |
| 2281 Handle<Object> cmd_processor = Execution::Call( | |
| 2282 isolate_, cmd_processor_ctor, exec_state, 1, ctor_args).ToHandleChecked(); | |
| 2283 Handle<JSFunction> process_debug_request = Handle<JSFunction>::cast( | |
| 2284 Object::GetProperty( | |
| 2285 isolate_, cmd_processor, "processDebugRequest").ToHandleChecked()); | |
| 2286 Handle<Object> is_running = Object::GetProperty( | |
| 2287 isolate_, cmd_processor, "isRunning").ToHandleChecked(); | |
| 2288 | |
| 2289 // Process requests from the debugger. | |
| 2290 do { | |
| 2291 // Wait for new command in the queue. | |
| 2292 command_received_.Wait(); | |
| 2293 | |
| 2294 // Get the command from the queue. | |
| 2295 CommandMessage command = command_queue_.Get(); | |
| 2296 isolate_->logger()->DebugTag( | |
| 2297 "Got request from command queue, in interactive loop."); | |
| 2298 if (!is_active()) { | |
| 2299 // Delete command text and user data. | |
| 2300 command.Dispose(); | |
| 2301 return; | |
| 2302 } | |
| 2303 | |
| 2304 Vector<const uc16> command_text( | |
| 2305 const_cast<const uc16*>(command.text().start()), | |
| 2306 command.text().length()); | |
| 2307 Handle<String> request_text = isolate_->factory()->NewStringFromTwoByte( | |
| 2308 command_text).ToHandleChecked(); | |
| 2309 Handle<Object> request_args[] = { request_text }; | |
| 2310 Handle<Object> answer_value; | |
| 2311 Handle<String> answer; | |
| 2312 MaybeHandle<Object> maybe_exception; | |
| 2313 MaybeHandle<Object> maybe_result = | |
| 2314 Execution::TryCall(process_debug_request, cmd_processor, 1, | |
| 2315 request_args, &maybe_exception); | |
| 2316 | |
| 2317 if (maybe_result.ToHandle(&answer_value)) { | |
| 2318 if (answer_value->IsUndefined()) { | |
| 2319 answer = isolate_->factory()->empty_string(); | |
| 2320 } else { | |
| 2321 answer = Handle<String>::cast(answer_value); | |
| 2322 } | |
| 2323 | |
| 2324 // Log the JSON request/response. | |
| 2325 if (FLAG_trace_debug_json) { | |
| 2326 PrintF("%s\n", request_text->ToCString().get()); | |
| 2327 PrintF("%s\n", answer->ToCString().get()); | |
| 2328 } | |
| 2329 | |
| 2330 Handle<Object> is_running_args[] = { answer }; | |
| 2331 maybe_result = Execution::Call( | |
| 2332 isolate_, is_running, cmd_processor, 1, is_running_args); | |
| 2333 Handle<Object> result; | |
| 2334 if (!maybe_result.ToHandle(&result)) break; | |
| 2335 running = result->IsTrue(); | |
| 2336 } else { | |
| 2337 Handle<Object> exception; | |
| 2338 if (!maybe_exception.ToHandle(&exception)) break; | |
| 2339 Handle<Object> result; | |
| 2340 if (!Execution::ToString(isolate_, exception).ToHandle(&result)) break; | |
| 2341 answer = Handle<String>::cast(result); | |
| 2342 } | |
| 2343 | |
| 2344 // Return the result. | |
| 2345 MessageImpl message = MessageImpl::NewResponse( | |
| 2346 event, running, exec_state, event_data, answer, command.client_data()); | |
| 2347 InvokeMessageHandler(message); | |
| 2348 command.Dispose(); | |
| 2349 | |
| 2350 // Return from debug event processing if either the VM is put into the | |
| 2351 // running state (through a continue command) or auto continue is active | |
| 2352 // and there are no more commands queued. | |
| 2353 } while (!running || has_commands()); | |
| 2354 command_queue_.Clear(); | |
| 2355 } | |
| 2356 | |
| 2357 | |
| 2358 void Debug::SetEventListener(Handle<Object> callback, | |
| 2359 Handle<Object> data) { | |
| 2360 GlobalHandles* global_handles = isolate_->global_handles(); | |
| 2361 | |
| 2362 // Remove existing entry. | |
| 2363 GlobalHandles::Destroy(event_listener_.location()); | |
| 2364 event_listener_ = Handle<Object>(); | |
| 2365 GlobalHandles::Destroy(event_listener_data_.location()); | |
| 2366 event_listener_data_ = Handle<Object>(); | |
| 2367 | |
| 2368 // Set new entry. | |
| 2369 if (!callback->IsUndefined() && !callback->IsNull()) { | |
| 2370 event_listener_ = global_handles->Create(*callback); | |
| 2371 if (data.is_null()) data = isolate_->factory()->undefined_value(); | |
| 2372 event_listener_data_ = global_handles->Create(*data); | |
| 2373 } | |
| 2374 | |
| 2375 UpdateState(); | |
| 2376 } | |
| 2377 | |
| 2378 | |
| 2379 void Debug::SetMessageHandler(v8::Debug::MessageHandler handler) { | |
| 2380 message_handler_ = handler; | |
| 2381 UpdateState(); | |
| 2382 if (handler == NULL && in_debug_scope()) { | |
| 2383 // Send an empty command to the debugger if in a break to make JavaScript | |
| 2384 // run again if the debugger is closed. | |
| 2385 EnqueueCommandMessage(Vector<const uint16_t>::empty()); | |
| 2386 } | |
| 2387 } | |
| 2388 | |
| 2389 | |
| 2390 | |
| 2391 void Debug::UpdateState() { | |
| 2392 bool is_active = message_handler_ != NULL || !event_listener_.is_null(); | |
| 2393 if (is_active || in_debug_scope()) { | |
| 2394 // Note that the debug context could have already been loaded to | |
| 2395 // bootstrap test cases. | |
| 2396 isolate_->compilation_cache()->Disable(); | |
| 2397 is_active = Load(); | |
| 2398 } else if (is_loaded()) { | |
| 2399 isolate_->compilation_cache()->Enable(); | |
| 2400 Unload(); | |
| 2401 } | |
| 2402 is_active_ = is_active; | |
| 2403 } | |
| 2404 | |
| 2405 | |
| 2406 // Calls the registered debug message handler. This callback is part of the | |
| 2407 // public API. | |
| 2408 void Debug::InvokeMessageHandler(MessageImpl message) { | |
| 2409 if (message_handler_ != NULL) message_handler_(message); | |
| 2410 } | |
| 2411 | |
| 2412 | |
| 2413 // Puts a command coming from the public API on the queue. Creates | |
| 2414 // a copy of the command string managed by the debugger. Up to this | |
| 2415 // point, the command data was managed by the API client. Called | |
| 2416 // by the API client thread. | |
| 2417 void Debug::EnqueueCommandMessage(Vector<const uint16_t> command, | |
| 2418 v8::Debug::ClientData* client_data) { | |
| 2419 // Need to cast away const. | |
| 2420 CommandMessage message = CommandMessage::New( | |
| 2421 Vector<uint16_t>(const_cast<uint16_t*>(command.start()), | |
| 2422 command.length()), | |
| 2423 client_data); | |
| 2424 isolate_->logger()->DebugTag("Put command on command_queue."); | |
| 2425 command_queue_.Put(message); | |
| 2426 command_received_.Signal(); | |
| 2427 | |
| 2428 // Set the debug command break flag to have the command processed. | |
| 2429 if (!in_debug_scope()) isolate_->stack_guard()->RequestDebugCommand(); | |
| 2430 } | |
| 2431 | |
| 2432 | |
| 2433 MaybeHandle<Object> Debug::Call(Handle<JSFunction> fun, Handle<Object> data) { | |
| 2434 DebugScope debug_scope(this); | |
| 2435 if (debug_scope.failed()) return isolate_->factory()->undefined_value(); | |
| 2436 | |
| 2437 // Create the execution state. | |
| 2438 Handle<Object> exec_state; | |
| 2439 if (!MakeExecutionState().ToHandle(&exec_state)) { | |
| 2440 return isolate_->factory()->undefined_value(); | |
| 2441 } | |
| 2442 | |
| 2443 Handle<Object> argv[] = { exec_state, data }; | |
| 2444 return Execution::Call( | |
| 2445 isolate_, | |
| 2446 fun, | |
| 2447 Handle<Object>(debug_context()->global_proxy(), isolate_), | |
| 2448 arraysize(argv), | |
| 2449 argv); | |
| 2450 } | |
| 2451 | |
| 2452 | |
| 2453 void Debug::HandleDebugBreak() { | |
| 2454 // Ignore debug break during bootstrapping. | |
| 2455 if (isolate_->bootstrapper()->IsActive()) return; | |
| 2456 // Just continue if breaks are disabled. | |
| 2457 if (break_disabled()) return; | |
| 2458 // Ignore debug break if debugger is not active. | |
| 2459 if (!is_active()) return; | |
| 2460 | |
| 2461 StackLimitCheck check(isolate_); | |
| 2462 if (check.HasOverflowed()) return; | |
| 2463 | |
| 2464 { JavaScriptFrameIterator it(isolate_); | |
| 2465 DCHECK(!it.done()); | |
| 2466 Object* fun = it.frame()->function(); | |
| 2467 if (fun && fun->IsJSFunction()) { | |
| 2468 // Don't stop in builtin functions. | |
| 2469 if (JSFunction::cast(fun)->IsBuiltin()) return; | |
| 2470 GlobalObject* global = JSFunction::cast(fun)->context()->global_object(); | |
| 2471 // Don't stop in debugger functions. | |
| 2472 if (IsDebugGlobal(global)) return; | |
| 2473 } | |
| 2474 } | |
| 2475 | |
| 2476 // Collect the break state before clearing the flags. | |
| 2477 bool debug_command_only = isolate_->stack_guard()->CheckDebugCommand() && | |
| 2478 !isolate_->stack_guard()->CheckDebugBreak(); | |
| 2479 | |
| 2480 isolate_->stack_guard()->ClearDebugBreak(); | |
| 2481 | |
| 2482 ProcessDebugMessages(debug_command_only); | |
| 2483 } | |
| 2484 | |
| 2485 | |
| 2486 void Debug::ProcessDebugMessages(bool debug_command_only) { | |
| 2487 isolate_->stack_guard()->ClearDebugCommand(); | |
| 2488 | |
| 2489 StackLimitCheck check(isolate_); | |
| 2490 if (check.HasOverflowed()) return; | |
| 2491 | |
| 2492 HandleScope scope(isolate_); | |
| 2493 DebugScope debug_scope(this); | |
| 2494 if (debug_scope.failed()) return; | |
| 2495 | |
| 2496 // Notify the debug event listeners. Indicate auto continue if the break was | |
| 2497 // a debug command break. | |
| 2498 OnDebugBreak(isolate_->factory()->undefined_value(), debug_command_only); | |
| 2499 } | |
| 2500 | |
| 2501 | |
| 2502 DebugScope::DebugScope(Debug* debug) | |
| 2503 : debug_(debug), | |
| 2504 prev_(debug->debugger_entry()), | |
| 2505 save_(debug_->isolate_), | |
| 2506 no_termination_exceptons_(debug_->isolate_, | |
| 2507 StackGuard::TERMINATE_EXECUTION) { | |
| 2508 // Link recursive debugger entry. | |
| 2509 base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_, | |
| 2510 reinterpret_cast<base::AtomicWord>(this)); | |
| 2511 | |
| 2512 // Store the previous break id and frame id. | |
| 2513 break_id_ = debug_->break_id(); | |
| 2514 break_frame_id_ = debug_->break_frame_id(); | |
| 2515 | |
| 2516 // Create the new break info. If there is no JavaScript frames there is no | |
| 2517 // break frame id. | |
| 2518 JavaScriptFrameIterator it(isolate()); | |
| 2519 bool has_js_frames = !it.done(); | |
| 2520 debug_->thread_local_.break_frame_id_ = has_js_frames ? it.frame()->id() | |
| 2521 : StackFrame::NO_ID; | |
| 2522 debug_->SetNextBreakId(); | |
| 2523 | |
| 2524 debug_->UpdateState(); | |
| 2525 // Make sure that debugger is loaded and enter the debugger context. | |
| 2526 // The previous context is kept in save_. | |
| 2527 failed_ = !debug_->is_loaded(); | |
| 2528 if (!failed_) isolate()->set_context(*debug->debug_context()); | |
| 2529 } | |
| 2530 | |
| 2531 | |
| 2532 | |
| 2533 DebugScope::~DebugScope() { | |
| 2534 if (!failed_ && prev_ == NULL) { | |
| 2535 // Clear mirror cache when leaving the debugger. Skip this if there is a | |
| 2536 // pending exception as clearing the mirror cache calls back into | |
| 2537 // JavaScript. This can happen if the v8::Debug::Call is used in which | |
| 2538 // case the exception should end up in the calling code. | |
| 2539 if (!isolate()->has_pending_exception()) debug_->ClearMirrorCache(); | |
| 2540 | |
| 2541 // If there are commands in the queue when leaving the debugger request | |
| 2542 // that these commands are processed. | |
| 2543 if (debug_->has_commands()) isolate()->stack_guard()->RequestDebugCommand(); | |
| 2544 } | |
| 2545 | |
| 2546 // Leaving this debugger entry. | |
| 2547 base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_, | |
| 2548 reinterpret_cast<base::AtomicWord>(prev_)); | |
| 2549 | |
| 2550 // Restore to the previous break state. | |
| 2551 debug_->thread_local_.break_frame_id_ = break_frame_id_; | |
| 2552 debug_->thread_local_.break_id_ = break_id_; | |
| 2553 | |
| 2554 debug_->UpdateState(); | |
| 2555 } | |
| 2556 | |
| 2557 | |
| 2558 MessageImpl MessageImpl::NewEvent(DebugEvent event, | |
| 2559 bool running, | |
| 2560 Handle<JSObject> exec_state, | |
| 2561 Handle<JSObject> event_data) { | |
| 2562 MessageImpl message(true, event, running, | |
| 2563 exec_state, event_data, Handle<String>(), NULL); | |
| 2564 return message; | |
| 2565 } | |
| 2566 | |
| 2567 | |
| 2568 MessageImpl MessageImpl::NewResponse(DebugEvent event, | |
| 2569 bool running, | |
| 2570 Handle<JSObject> exec_state, | |
| 2571 Handle<JSObject> event_data, | |
| 2572 Handle<String> response_json, | |
| 2573 v8::Debug::ClientData* client_data) { | |
| 2574 MessageImpl message(false, event, running, | |
| 2575 exec_state, event_data, response_json, client_data); | |
| 2576 return message; | |
| 2577 } | |
| 2578 | |
| 2579 | |
| 2580 MessageImpl::MessageImpl(bool is_event, | |
| 2581 DebugEvent event, | |
| 2582 bool running, | |
| 2583 Handle<JSObject> exec_state, | |
| 2584 Handle<JSObject> event_data, | |
| 2585 Handle<String> response_json, | |
| 2586 v8::Debug::ClientData* client_data) | |
| 2587 : is_event_(is_event), | |
| 2588 event_(event), | |
| 2589 running_(running), | |
| 2590 exec_state_(exec_state), | |
| 2591 event_data_(event_data), | |
| 2592 response_json_(response_json), | |
| 2593 client_data_(client_data) {} | |
| 2594 | |
| 2595 | |
| 2596 bool MessageImpl::IsEvent() const { | |
| 2597 return is_event_; | |
| 2598 } | |
| 2599 | |
| 2600 | |
| 2601 bool MessageImpl::IsResponse() const { | |
| 2602 return !is_event_; | |
| 2603 } | |
| 2604 | |
| 2605 | |
| 2606 DebugEvent MessageImpl::GetEvent() const { | |
| 2607 return event_; | |
| 2608 } | |
| 2609 | |
| 2610 | |
| 2611 bool MessageImpl::WillStartRunning() const { | |
| 2612 return running_; | |
| 2613 } | |
| 2614 | |
| 2615 | |
| 2616 v8::Local<v8::Object> MessageImpl::GetExecutionState() const { | |
| 2617 return v8::Utils::ToLocal(exec_state_); | |
| 2618 } | |
| 2619 | |
| 2620 | |
| 2621 v8::Isolate* MessageImpl::GetIsolate() const { | |
| 2622 return reinterpret_cast<v8::Isolate*>(exec_state_->GetIsolate()); | |
| 2623 } | |
| 2624 | |
| 2625 | |
| 2626 v8::Local<v8::Object> MessageImpl::GetEventData() const { | |
| 2627 return v8::Utils::ToLocal(event_data_); | |
| 2628 } | |
| 2629 | |
| 2630 | |
| 2631 v8::Local<v8::String> MessageImpl::GetJSON() const { | |
| 2632 Isolate* isolate = event_data_->GetIsolate(); | |
| 2633 v8::EscapableHandleScope scope(reinterpret_cast<v8::Isolate*>(isolate)); | |
| 2634 | |
| 2635 if (IsEvent()) { | |
| 2636 // Call toJSONProtocol on the debug event object. | |
| 2637 Handle<Object> fun = Object::GetProperty( | |
| 2638 isolate, event_data_, "toJSONProtocol").ToHandleChecked(); | |
| 2639 if (!fun->IsJSFunction()) { | |
| 2640 return v8::Local<v8::String>(); | |
| 2641 } | |
| 2642 | |
| 2643 MaybeHandle<Object> maybe_json = | |
| 2644 Execution::TryCall(Handle<JSFunction>::cast(fun), event_data_, 0, NULL); | |
| 2645 Handle<Object> json; | |
| 2646 if (!maybe_json.ToHandle(&json) || !json->IsString()) { | |
| 2647 return v8::Local<v8::String>(); | |
| 2648 } | |
| 2649 return scope.Escape(v8::Utils::ToLocal(Handle<String>::cast(json))); | |
| 2650 } else { | |
| 2651 return v8::Utils::ToLocal(response_json_); | |
| 2652 } | |
| 2653 } | |
| 2654 | |
| 2655 | |
| 2656 v8::Local<v8::Context> MessageImpl::GetEventContext() const { | |
| 2657 Isolate* isolate = event_data_->GetIsolate(); | |
| 2658 v8::Local<v8::Context> context = GetDebugEventContext(isolate); | |
| 2659 // Isolate::context() may be NULL when "script collected" event occurs. | |
| 2660 DCHECK(!context.IsEmpty()); | |
| 2661 return context; | |
| 2662 } | |
| 2663 | |
| 2664 | |
| 2665 v8::Debug::ClientData* MessageImpl::GetClientData() const { | |
| 2666 return client_data_; | |
| 2667 } | |
| 2668 | |
| 2669 | |
| 2670 EventDetailsImpl::EventDetailsImpl(DebugEvent event, | |
| 2671 Handle<JSObject> exec_state, | |
| 2672 Handle<JSObject> event_data, | |
| 2673 Handle<Object> callback_data, | |
| 2674 v8::Debug::ClientData* client_data) | |
| 2675 : event_(event), | |
| 2676 exec_state_(exec_state), | |
| 2677 event_data_(event_data), | |
| 2678 callback_data_(callback_data), | |
| 2679 client_data_(client_data) {} | |
| 2680 | |
| 2681 | |
| 2682 DebugEvent EventDetailsImpl::GetEvent() const { | |
| 2683 return event_; | |
| 2684 } | |
| 2685 | |
| 2686 | |
| 2687 v8::Local<v8::Object> EventDetailsImpl::GetExecutionState() const { | |
| 2688 return v8::Utils::ToLocal(exec_state_); | |
| 2689 } | |
| 2690 | |
| 2691 | |
| 2692 v8::Local<v8::Object> EventDetailsImpl::GetEventData() const { | |
| 2693 return v8::Utils::ToLocal(event_data_); | |
| 2694 } | |
| 2695 | |
| 2696 | |
| 2697 v8::Local<v8::Context> EventDetailsImpl::GetEventContext() const { | |
| 2698 return GetDebugEventContext(exec_state_->GetIsolate()); | |
| 2699 } | |
| 2700 | |
| 2701 | |
| 2702 v8::Local<v8::Value> EventDetailsImpl::GetCallbackData() const { | |
| 2703 return v8::Utils::ToLocal(callback_data_); | |
| 2704 } | |
| 2705 | |
| 2706 | |
| 2707 v8::Debug::ClientData* EventDetailsImpl::GetClientData() const { | |
| 2708 return client_data_; | |
| 2709 } | |
| 2710 | |
| 2711 | |
| 2712 CommandMessage::CommandMessage() : text_(Vector<uint16_t>::empty()), | |
| 2713 client_data_(NULL) { | |
| 2714 } | |
| 2715 | |
| 2716 | |
| 2717 CommandMessage::CommandMessage(const Vector<uint16_t>& text, | |
| 2718 v8::Debug::ClientData* data) | |
| 2719 : text_(text), | |
| 2720 client_data_(data) { | |
| 2721 } | |
| 2722 | |
| 2723 | |
| 2724 void CommandMessage::Dispose() { | |
| 2725 text_.Dispose(); | |
| 2726 delete client_data_; | |
| 2727 client_data_ = NULL; | |
| 2728 } | |
| 2729 | |
| 2730 | |
| 2731 CommandMessage CommandMessage::New(const Vector<uint16_t>& command, | |
| 2732 v8::Debug::ClientData* data) { | |
| 2733 return CommandMessage(command.Clone(), data); | |
| 2734 } | |
| 2735 | |
| 2736 | |
| 2737 CommandMessageQueue::CommandMessageQueue(int size) : start_(0), end_(0), | |
| 2738 size_(size) { | |
| 2739 messages_ = NewArray<CommandMessage>(size); | |
| 2740 } | |
| 2741 | |
| 2742 | |
| 2743 CommandMessageQueue::~CommandMessageQueue() { | |
| 2744 while (!IsEmpty()) Get().Dispose(); | |
| 2745 DeleteArray(messages_); | |
| 2746 } | |
| 2747 | |
| 2748 | |
| 2749 CommandMessage CommandMessageQueue::Get() { | |
| 2750 DCHECK(!IsEmpty()); | |
| 2751 int result = start_; | |
| 2752 start_ = (start_ + 1) % size_; | |
| 2753 return messages_[result]; | |
| 2754 } | |
| 2755 | |
| 2756 | |
| 2757 void CommandMessageQueue::Put(const CommandMessage& message) { | |
| 2758 if ((end_ + 1) % size_ == start_) { | |
| 2759 Expand(); | |
| 2760 } | |
| 2761 messages_[end_] = message; | |
| 2762 end_ = (end_ + 1) % size_; | |
| 2763 } | |
| 2764 | |
| 2765 | |
| 2766 void CommandMessageQueue::Expand() { | |
| 2767 CommandMessageQueue new_queue(size_ * 2); | |
| 2768 while (!IsEmpty()) { | |
| 2769 new_queue.Put(Get()); | |
| 2770 } | |
| 2771 CommandMessage* array_to_free = messages_; | |
| 2772 *this = new_queue; | |
| 2773 new_queue.messages_ = array_to_free; | |
| 2774 // Make the new_queue empty so that it doesn't call Dispose on any messages. | |
| 2775 new_queue.start_ = new_queue.end_; | |
| 2776 // Automatic destructor called on new_queue, freeing array_to_free. | |
| 2777 } | |
| 2778 | |
| 2779 | |
| 2780 LockingCommandMessageQueue::LockingCommandMessageQueue(Logger* logger, int size) | |
| 2781 : logger_(logger), queue_(size) {} | |
| 2782 | |
| 2783 | |
| 2784 bool LockingCommandMessageQueue::IsEmpty() const { | |
| 2785 base::LockGuard<base::Mutex> lock_guard(&mutex_); | |
| 2786 return queue_.IsEmpty(); | |
| 2787 } | |
| 2788 | |
| 2789 | |
| 2790 CommandMessage LockingCommandMessageQueue::Get() { | |
| 2791 base::LockGuard<base::Mutex> lock_guard(&mutex_); | |
| 2792 CommandMessage result = queue_.Get(); | |
| 2793 logger_->DebugEvent("Get", result.text()); | |
| 2794 return result; | |
| 2795 } | |
| 2796 | |
| 2797 | |
| 2798 void LockingCommandMessageQueue::Put(const CommandMessage& message) { | |
| 2799 base::LockGuard<base::Mutex> lock_guard(&mutex_); | |
| 2800 queue_.Put(message); | |
| 2801 logger_->DebugEvent("Put", message.text()); | |
| 2802 } | |
| 2803 | |
| 2804 | |
| 2805 void LockingCommandMessageQueue::Clear() { | |
| 2806 base::LockGuard<base::Mutex> lock_guard(&mutex_); | |
| 2807 queue_.Clear(); | |
| 2808 } | |
| 2809 | |
| 2810 } // namespace internal | |
| 2811 } // namespace v8 | |
| OLD | NEW |