Changeset 158937 in webkit for trunk/Source/JavaScriptCore/debugger/Debugger.cpp
- Timestamp:
- Nov 8, 2013, 12:03:50 PM (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/debugger/Debugger.cpp
r157746 r158937 1 1 /* 2 * Copyright (C) 2008 Apple Inc. All rights reserved.2 * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. 3 3 * Copyright (C) 1999-2001 Harri Porten ([email protected]) 4 4 * Copyright (C) 2001 Peter Kelly ([email protected]) … … 23 23 #include "Debugger.h" 24 24 25 #include "DebuggerCallFrame.h" 25 26 #include "Error.h" 26 27 #include "HeapIterationScope.h" 27 28 #include "Interpreter.h" 29 #include "JSCJSValueInlines.h" 28 30 #include "JSFunction.h" 29 31 #include "JSGlobalObject.h" … … 92 94 namespace JSC { 93 95 94 Debugger::Debugger() 95 : m_needsExceptionCallbacks(false) 96 class DebuggerCallFrameScope { 97 public: 98 DebuggerCallFrameScope(Debugger& debugger) 99 : m_debugger(debugger) 100 { 101 ASSERT(!m_debugger.m_currentDebuggerCallFrame); 102 if (m_debugger.m_currentCallFrame) 103 m_debugger.m_currentDebuggerCallFrame = DebuggerCallFrame::create(debugger.m_currentCallFrame); 104 } 105 106 ~DebuggerCallFrameScope() 107 { 108 if (m_debugger.m_currentDebuggerCallFrame) { 109 m_debugger.m_currentDebuggerCallFrame->invalidate(); 110 m_debugger.m_currentDebuggerCallFrame = 0; 111 } 112 } 113 114 private: 115 Debugger& m_debugger; 116 }; 117 118 119 Debugger::Debugger(bool isInWorkerThread) 120 : m_pauseOnExceptionsState(DontPauseOnExceptions) 121 , m_pauseOnNextStatement(false) 122 , m_isPaused(false) 123 , m_breakpointsActivated(true) 124 , m_hasHandlerForExceptionCallback(false) 125 , m_isInWorkerThread(isInWorkerThread) 126 , m_reasonForPause(NotPaused) 127 , m_pauseOnCallFrame(0) 128 , m_currentCallFrame(0) 129 , m_lastExecutedLine(UINT_MAX) 130 , m_lastExecutedSourceID(noSourceID) 131 , m_topBreakpointID(noBreakpointID) 96 132 , m_needsOpDebugCallbacks(false) 97 133 , m_shouldPause(false) 98 , m_numberOfBreakpoints(0)99 134 { 100 135 } … … 116 151 void Debugger::detach(JSGlobalObject* globalObject) 117 152 { 153 // If we're detaching from the currently executing global object, manually tear down our 154 // stack, since we won't get further debugger callbacks to do so. Also, resume execution, 155 // since there's no point in staying paused once a window closes. 156 if (m_currentCallFrame && m_currentCallFrame->dynamicGlobalObject() == globalObject) { 157 m_currentCallFrame = 0; 158 m_pauseOnCallFrame = 0; 159 continueProgram(); 160 } 161 118 162 ASSERT(m_globalObjects.contains(globalObject)); 119 163 m_globalObjects.remove(globalObject); 120 164 globalObject->setDebugger(0); 121 }122 123 void Debugger::setNeedsExceptionCallbacks(bool value)124 {125 m_needsExceptionCallbacks = value;126 165 } 127 166 … … 147 186 } 148 187 149 void Debugger::updateNumberOfBreakpoints(int numberOfBreakpoints) 150 { 151 ASSERT(numberOfBreakpoints >= 0); 152 m_numberOfBreakpoints = numberOfBreakpoints; 188 void Debugger::updateNeedForOpDebugCallbacks() 189 { 190 size_t numberOfBreakpoints = m_breakpointIDToBreakpoint.size(); 191 m_needsOpDebugCallbacks = m_shouldPause || numberOfBreakpoints; 192 } 193 194 BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine, unsigned& actualColumn) 195 { 196 SourceID sourceID = breakpoint.sourceID; 197 unsigned line = breakpoint.line; 198 unsigned column = breakpoint.column; 199 200 SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID); 201 if (it == m_sourceIDToBreakpoints.end()) 202 it = m_sourceIDToBreakpoints.set(sourceID, LineToBreakpointsMap()).iterator; 203 LineToBreakpointsMap::iterator breaksIt = it->value.find(line); 204 if (breaksIt == it->value.end()) 205 breaksIt = it->value.set(line, BreakpointsInLine()).iterator; 206 207 BreakpointsInLine& breakpoints = breaksIt->value; 208 unsigned breakpointsCount = breakpoints.size(); 209 for (unsigned i = 0; i < breakpointsCount; i++) 210 if (breakpoints[i].column == column) { 211 // The breakpoint already exists. We're not allowed to create a new 212 // breakpoint at this location. Rather than returning the breakpointID 213 // of the pre-existing breakpoint, we need to return noBreakpointID 214 // to indicate that we're not creating a new one. 215 return noBreakpointID; 216 } 217 218 BreakpointID id = ++m_topBreakpointID; 219 RELEASE_ASSERT(id != noBreakpointID); 220 221 breakpoint.id = id; 222 actualLine = line; 223 actualColumn = column; 224 225 breakpoints.append(breakpoint); 226 m_breakpointIDToBreakpoint.set(id, &breakpoints.last()); 227 153 228 updateNeedForOpDebugCallbacks(); 154 } 155 156 void Debugger::updateNeedForOpDebugCallbacks() 157 { 158 m_needsOpDebugCallbacks = m_shouldPause || m_numberOfBreakpoints; 229 230 return id; 231 } 232 233 void Debugger::removeBreakpoint(BreakpointID id) 234 { 235 ASSERT(id != noBreakpointID); 236 237 BreakpointIDToBreakpointMap::iterator idIt = m_breakpointIDToBreakpoint.find(id); 238 ASSERT(idIt != m_breakpointIDToBreakpoint.end()); 239 Breakpoint& breakpoint = *idIt->value; 240 241 SourceID sourceID = breakpoint.sourceID; 242 ASSERT(sourceID); 243 SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID); 244 ASSERT(it != m_sourceIDToBreakpoints.end()); 245 LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint.line); 246 ASSERT(breaksIt != it->value.end()); 247 248 BreakpointsInLine& breakpoints = breaksIt->value; 249 unsigned breakpointsCount = breakpoints.size(); 250 for (unsigned i = 0; i < breakpointsCount; i++) { 251 if (breakpoints[i].id == breakpoint.id) { 252 breakpoints.remove(i); 253 m_breakpointIDToBreakpoint.remove(idIt); 254 255 if (breakpoints.isEmpty()) { 256 it->value.remove(breaksIt); 257 if (it->value.isEmpty()) 258 m_sourceIDToBreakpoints.remove(it); 259 } 260 break; 261 } 262 } 263 264 updateNeedForOpDebugCallbacks(); 265 } 266 267 bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Breakpoint *hitBreakpoint) const 268 { 269 if (!m_breakpointsActivated) 270 return false; 271 272 SourceIDToBreakpointsMap::const_iterator it = m_sourceIDToBreakpoints.find(sourceID); 273 if (it == m_sourceIDToBreakpoints.end()) 274 return false; 275 276 unsigned line = position.m_line.zeroBasedInt(); 277 unsigned column = position.m_column.zeroBasedInt(); 278 279 LineToBreakpointsMap::const_iterator breaksIt = it->value.find(line); 280 if (breaksIt == it->value.end()) 281 return false; 282 283 bool hit = false; 284 const BreakpointsInLine& breakpoints = breaksIt->value; 285 unsigned breakpointsCount = breakpoints.size(); 286 unsigned i; 287 for (i = 0; i < breakpointsCount; i++) { 288 unsigned breakLine = breakpoints[i].line; 289 unsigned breakColumn = breakpoints[i].column; 290 // Since frontend truncates the indent, the first statement in a line must match the breakpoint (line,0). 291 if ((line != m_lastExecutedLine && line == breakLine && !breakColumn) 292 || (line == breakLine && column == breakColumn)) { 293 hit = true; 294 break; 295 } 296 } 297 if (!hit) 298 return false; 299 300 if (hitBreakpoint) 301 *hitBreakpoint = breakpoints[i]; 302 303 if (breakpoints[i].condition.isEmpty()) 304 return true; 305 306 JSValue exception; 307 JSValue result = DebuggerCallFrame::evaluateWithCallFrame(m_currentCallFrame, breakpoints[i].condition, exception); 308 if (exception) { 309 // An erroneous condition counts as "false". 310 handleExceptionInBreakpointCondition(m_currentCallFrame, exception); 311 return false; 312 } 313 return result.toBoolean(m_currentCallFrame); 314 } 315 316 void Debugger::clearBreakpoints() 317 { 318 m_topBreakpointID = noBreakpointID; 319 m_breakpointIDToBreakpoint.clear(); 320 m_sourceIDToBreakpoints.clear(); 321 322 updateNeedForOpDebugCallbacks(); 323 } 324 325 void Debugger::setBreakpointsActivated(bool activated) 326 { 327 m_breakpointsActivated = activated; 328 } 329 330 void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause) 331 { 332 m_pauseOnExceptionsState = pause; 333 } 334 335 void Debugger::setPauseOnNextStatement(bool pause) 336 { 337 m_pauseOnNextStatement = pause; 338 if (pause) 339 setShouldPause(true); 340 } 341 342 void Debugger::breakProgram() 343 { 344 if (m_isPaused || !m_currentCallFrame) 345 return; 346 347 m_pauseOnNextStatement = true; 348 setShouldPause(true); 349 pauseIfNeeded(m_currentCallFrame); 350 } 351 352 void Debugger::continueProgram() 353 { 354 if (!m_isPaused) 355 return; 356 357 m_pauseOnNextStatement = false; 358 notifyDoneProcessingDebuggerEvents(); 359 } 360 361 void Debugger::stepIntoStatement() 362 { 363 if (!m_isPaused) 364 return; 365 366 m_pauseOnNextStatement = true; 367 setShouldPause(true); 368 notifyDoneProcessingDebuggerEvents(); 369 } 370 371 void Debugger::stepOverStatement() 372 { 373 if (!m_isPaused) 374 return; 375 376 m_pauseOnCallFrame = m_currentCallFrame; 377 notifyDoneProcessingDebuggerEvents(); 378 } 379 380 void Debugger::stepOutOfFunction() 381 { 382 if (!m_isPaused) 383 return; 384 385 m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrameSkippingVMEntrySentinel() : 0; 386 notifyDoneProcessingDebuggerEvents(); 387 } 388 389 void Debugger::updateCallFrame(CallFrame* callFrame) 390 { 391 m_currentCallFrame = callFrame; 392 SourceID sourceID = DebuggerCallFrame::sourceIDForCallFrame(callFrame); 393 if (m_lastExecutedSourceID != sourceID) { 394 m_lastExecutedLine = UINT_MAX; 395 m_lastExecutedSourceID = sourceID; 396 } 397 } 398 399 void Debugger::updateCallFrameAndPauseIfNeeded(CallFrame* callFrame) 400 { 401 updateCallFrame(callFrame); 402 pauseIfNeeded(callFrame); 403 if (!needsOpDebugCallbacks()) 404 m_currentCallFrame = 0; 405 } 406 407 void Debugger::pauseIfNeeded(CallFrame* callFrame) 408 { 409 if (m_isPaused) 410 return; 411 412 JSGlobalObject* dynamicGlobalObject = callFrame->dynamicGlobalObject(); 413 if (!needPauseHandling(dynamicGlobalObject)) 414 return; 415 416 Breakpoint breakpoint; 417 bool didHitBreakpoint = false; 418 bool pauseNow = m_pauseOnNextStatement; 419 pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame); 420 421 intptr_t sourceID = DebuggerCallFrame::sourceIDForCallFrame(m_currentCallFrame); 422 TextPosition position = DebuggerCallFrame::positionForCallFrame(m_currentCallFrame); 423 pauseNow |= didHitBreakpoint = hasBreakpoint(sourceID, position, &breakpoint); 424 m_lastExecutedLine = position.m_line.zeroBasedInt(); 425 if (!pauseNow) 426 return; 427 428 DebuggerCallFrameScope debuggerCallFrameScope(*this); 429 430 if (didHitBreakpoint) { 431 handleBreakpointHit(breakpoint); 432 if (breakpoint.autoContinue) 433 return; 434 } 435 436 m_pauseOnCallFrame = 0; 437 m_pauseOnNextStatement = false; 438 m_isPaused = true; 439 440 handlePause(m_reasonForPause, dynamicGlobalObject); 441 442 if (!m_pauseOnNextStatement && !m_pauseOnCallFrame) { 443 setShouldPause(false); 444 if (!needsOpDebugCallbacks()) 445 m_currentCallFrame = 0; 446 } 447 448 m_isPaused = false; 449 } 450 451 void Debugger::exception(CallFrame* callFrame, JSValue exception, bool hasHandler) 452 { 453 if (m_isPaused) 454 return; 455 456 PauseReasonDeclaration reason(*this, PausedForException); 457 if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasHandler)) { 458 m_pauseOnNextStatement = true; 459 setShouldPause(true); 460 } 461 462 m_hasHandlerForExceptionCallback = true; 463 m_currentException = exception; 464 updateCallFrameAndPauseIfNeeded(callFrame); 465 m_currentException = JSValue(); 466 m_hasHandlerForExceptionCallback = false; 467 } 468 469 void Debugger::atStatement(CallFrame* callFrame) 470 { 471 if (m_isPaused) 472 return; 473 474 PauseReasonDeclaration reason(*this, PausedAtStatement); 475 updateCallFrameAndPauseIfNeeded(callFrame); 476 } 477 478 void Debugger::callEvent(CallFrame* callFrame) 479 { 480 if (m_isPaused) 481 return; 482 483 PauseReasonDeclaration reason(*this, PausedAfterCall); 484 updateCallFrameAndPauseIfNeeded(callFrame); 485 } 486 487 void Debugger::returnEvent(CallFrame* callFrame) 488 { 489 if (m_isPaused) 490 return; 491 492 PauseReasonDeclaration reason(*this, PausedBeforeReturn); 493 updateCallFrameAndPauseIfNeeded(callFrame); 494 495 // detach may have been called during pauseIfNeeded 496 if (!m_currentCallFrame) 497 return; 498 499 // Treat stepping over a return statement like stepping out. 500 if (m_currentCallFrame == m_pauseOnCallFrame) 501 m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); 502 503 m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); 504 } 505 506 void Debugger::willExecuteProgram(CallFrame* callFrame) 507 { 508 if (m_isPaused) 509 return; 510 511 PauseReasonDeclaration reason(*this, PausedAtStartOfProgram); 512 // FIXME: This check for whether we're debugging a worker thread is a workaround 513 // for https://bugs.webkit.org/show_bug.cgi?id=102637. Remove it when we rework 514 // the debugger implementation to not require callbacks. 515 if (!m_isInWorkerThread) 516 updateCallFrameAndPauseIfNeeded(callFrame); 517 else if (needsOpDebugCallbacks()) 518 updateCallFrame(callFrame); 519 } 520 521 void Debugger::didExecuteProgram(CallFrame* callFrame) 522 { 523 if (m_isPaused) 524 return; 525 526 PauseReasonDeclaration reason(*this, PausedAtEndOfProgram); 527 updateCallFrameAndPauseIfNeeded(callFrame); 528 529 // Treat stepping over the end of a program like stepping out. 530 if (!m_currentCallFrame) 531 return; 532 if (m_currentCallFrame == m_pauseOnCallFrame) { 533 m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); 534 if (!m_currentCallFrame) 535 return; 536 } 537 m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); 538 } 539 540 void Debugger::didReachBreakpoint(CallFrame* callFrame) 541 { 542 if (m_isPaused) 543 return; 544 545 PauseReasonDeclaration reason(*this, PausedForBreakpoint); 546 m_pauseOnNextStatement = true; 547 setShouldPause(true); 548 updateCallFrameAndPauseIfNeeded(callFrame); 549 } 550 551 DebuggerCallFrame* Debugger::currentDebuggerCallFrame() const 552 { 553 ASSERT(m_currentDebuggerCallFrame); 554 return m_currentDebuggerCallFrame.get(); 159 555 } 160 556
Note:
See TracChangeset
for help on using the changeset viewer.