'From Squeak5.3alpha of 1 October 2019 [latest update: #19012] on 1 October 2019 at 6:31:26 pm'!
CodeHolder subclass: #Debugger
	instanceVariableNames: 'interruptedProcess contextStack contextStackIndex contextStackList receiverInspector contextVariablesInspector externalInterrupt proceedValue selectingPC savedCursor isolationHead failedProject labelString message untilExpression '
	classVariableNames: 'ContextStackKeystrokes ErrorRecursion ErrorRecursionGuard ErrorReportServer FullStackSize InterruptUIProcessIfBlockedOnErrorInBackgroundProcess NotifierStackSize SavedExtent StackSizeLimit WantsAnnotationPane '
	poolDictionaries: ''
	category: 'Tools-Debugger'!
ProcessLocalVariable subclass: #DebuggerErrorRecursionFlag
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Tools-Debugger'!
TestCase subclass: #DebuggerTests
	instanceVariableNames: 'process debugger window'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'ToolsTests-Debugger'!

!DebuggerTests commentStamp: 'mt 10/1/2019 14:24' prior: 0!
You must be in a Morphic project to run these tests!! Note that *ALL* tests construct new processes and leave the current UI process as is. Even errors are injected in helper processes. So, the test runner will not suspend or terminate when running these tests.

Note that many of these tests depend on the StandardToolSet. So, if you have a custom tool set, maybe reset it first.!


!Context methodsFor: 'private' stamp: 'mt 10/1/2019 17:18'!
doPrimitive: primitiveIndex method: meth receiver: receiver args: arguments
	"Simulate a primitive method whose index is primitiveIndex.  The simulated receiver and
	 arguments are given as arguments to this message. If successful, push result and return
	 resuming context, else ^ {errCode, PrimitiveFailToken}. Any primitive which provokes
	 execution needs to be intercepted and simulated to avoid execution running away."

	| value |
	"Judicious use of primitive 19 (a null primitive that doesn't do anything) prevents
	 the debugger from entering various run-away activities such as spawning a new
	 process, etc.  Injudicious use results in the debugger not being able to debug
	 interesting code, such as the debugger itself.  Hence use primitive 19 with care :-)"
	"SystemNavigation new browseAllSelect: [:m| m primitive = 19]"
	primitiveIndex = 19 ifTrue: [
		Processor activeProcess
			debug: self
			title:'Code simulation error'
			full: false].

	((primitiveIndex between: 201 and: 222)
	 and: [(self objectClass: receiver) includesBehavior: BlockClosure]) ifTrue:
		[(primitiveIndex = 206
		  or: [primitiveIndex = 208]) ifTrue:						"[Full]BlockClosure>>valueWithArguments:"
			[^receiver simulateValueWithArguments: arguments first caller: self].
		 ((primitiveIndex between: 201 and: 209)			 "[Full]BlockClosure>>value[:value:...]"
		  or: [primitiveIndex between: 221 and: 222]) ifTrue: "[Full]BlockClosure>>valueNoContextSwitch[:]"
			[^receiver simulateValueWithArguments: arguments caller: self]].

	primitiveIndex = 83 ifTrue: "afr 9/11/1998 19:50" "Object>>perform:[with:...]"
		[^self send: arguments first to: receiver with: arguments allButFirst].
	primitiveIndex = 84 ifTrue: "afr 9/11/1998 19:50 & eem 8/18/2009 17:04" "Object>>perform:withArguments:"
		[^self send: arguments first to: receiver with: (arguments at: 2) lookupIn: (self objectClass: receiver)].
	primitiveIndex = 100 ifTrue: "eem 8/18/2009 16:57" "Object>>perform:withArguments:inSuperclass:"
		[^self send: arguments first to: receiver with: (arguments at: 2) lookupIn: (arguments at: 3)].

	"Mutex>>primitiveEnterCriticalSection
	 Mutex>>primitiveTestAndSetOwnershipOfCriticalSection"
	(primitiveIndex = 186 or: [primitiveIndex = 187]) ifTrue:
		[| effective |
		 effective := Processor activeProcess effectiveProcess.
		 "active == effective"
		 value := primitiveIndex = 186
					ifTrue: [receiver primitiveEnterCriticalSectionOnBehalfOf: effective]
					ifFalse: [receiver primitiveTestAndSetOwnershipOfCriticalSectionOnBehalfOf: effective].
		 ^(self isPrimFailToken: value)
			ifTrue: [value]
			ifFalse: [self push: value]].

	primitiveIndex = 188 ifTrue:	"Object>>withArgs:executeMethod:
									CompiledMethod class>>receiver:withArguments:executeMethod:
									VMMirror>>ifFail:object:with:executeMethod: et al"
		[| n args methodArg thisReceiver |
		 ((n := arguments size) between: 2 and: 4) ifFalse:
			[^self class primitiveFailTokenFor: #'unsupported operation'].
		 ((self objectClass: (args := arguments at: n - 1)) == Array
		  and: [(self objectClass: (methodArg := arguments at: n)) includesBehavior: CompiledMethod]) ifFalse:
			[^self class primitiveFailTokenFor: #'bad argument'].
		 methodArg numArgs = args size ifFalse:
			[^self class primitiveFailTokenFor: #'bad number of arguments'].
		 thisReceiver := arguments at: n - 2 ifAbsent: [receiver].
		 methodArg primitive > 0 ifTrue:
			[methodArg isQuick ifTrue:
				[^self push: (methodArg valueWithReceiver: thisReceiver arguments: args)].
			 ^self doPrimitive: methodArg primitive method: meth receiver: thisReceiver args: args].
		 ^Context
			sender: self
			receiver: thisReceiver
			method: methodArg
			arguments: args].

	primitiveIndex = 118 ifTrue: "[receiver:]tryPrimitive:withArgs:; avoid recursing in the VM"
		[(arguments size = 3
		  and: [(self objectClass: arguments second) == SmallInteger
		  and: [(self objectClass: arguments last) == Array]]) ifTrue:
			[^self doPrimitive: arguments second method: meth receiver: arguments first args: arguments last].
		 (arguments size = 2
		 and: [(self objectClass: arguments first) == SmallInteger
		 and: [(self objectClass: arguments last) == Array]]) ifFalse:
			[^self class primitiveFailTokenFor: nil].
		 ^self doPrimitive: arguments first method: meth receiver: receiver args: arguments last].

	value := primitiveIndex = 120 "FFI method"
				ifTrue: [(meth literalAt: 1) tryInvokeWithArguments: arguments]
				ifFalse:
					[primitiveIndex = 117 "named primitives"
						ifTrue: [self tryNamedPrimitiveIn: meth for: receiver withArgs: arguments]
						ifFalse: "should use self receiver: receiver tryPrimitive: primitiveIndex withArgs: arguments but this is only in later VMs (and appears to be broken)"
							[receiver tryPrimitive: primitiveIndex withArgs: arguments]].

	^(self isPrimFailToken: value)
		ifTrue: [value]
		ifFalse: [self push: value]! !

!Context methodsFor: 'private-exceptions' stamp: 'mt 10/1/2019 17:18'!
cannotReturn: result

	closureOrNil notNil ifTrue:
		[^self cannotReturn: result to: self home sender].
	Processor activeProcess
		debug: thisContext
		title: 'computation has been terminated'
		full: false.! !


!Debugger methodsFor: '*60Deprecated-private' stamp: 'mt 9/30/2019 17:39'!
lowSpaceChoices

	self deprecated.
	^ Smalltalk lowSpaceChoices! !

!Debugger methodsFor: 'actions - convenience' stamp: 'mt 10/1/2019 14:40'!
showFullStack

	self fullStack.! !

!Debugger methodsFor: 'actions - convenience' stamp: 'mt 10/1/2019 14:43'!
showWhere
	"Select the PC range"
	
	self where.! !

!Debugger methodsFor: 'actions - convenience' stamp: 'mt 10/1/2019 14:39'!
stepInto

	self send.! !

!Debugger methodsFor: 'actions - convenience' stamp: 'mt 10/1/2019 14:40'!
stepOver

	self doStep.! !

!Debugger methodsFor: 'actions - convenience' stamp: 'mt 10/1/2019 14:40'!
stepThrough

	self stepIntoBlock.! !

!Debugger methodsFor: 'initialize' stamp: 'mt 10/1/2019 10:51'!
close
	"Close and delete this project. Try to trigger the close request through the UI first, do manually of not in the UI."

	self topView
		ifNotNil: [self changed: #close]
		ifNil: [
			self okToClose ifTrue: [
				self windowIsClosing; release]].! !

!Debugger methodsFor: 'initialize' stamp: 'mt 10/1/2019 14:43'!
customButtonSpecs
	"Answer an array of elements of the form wording, selector, help-message, that characterize the custom button row of a debugger."

	| list |
	list := #(('Proceed'	proceed				'close the debugger and proceed.')
		('Restart'		restart				'reset this context to its start.')
		('Into'			stepInto				'step Into message sends'	interruptedProcessIsActive)
		('Over'			stepOver				'step Over message sends'	interruptedProcessIsActive)
		('Through'		stepThrough		'step into a block'			interruptedProcessIsActive)
		('Full Stack'		showFullStack			'show full stack')
		('Where'		showWhere				'select current pc range')
		('Tally'			tally				'time in milliseconds to execute')).
	Preferences restartAlsoProceeds ifTrue:
		[list := list collect: [:each |
			each second == #restart
				ifTrue: [each copy at: 3 put: 'proceed from the beginning of this context.'; yourself]
				ifFalse: [each]]].
	^ list! !

!Debugger methodsFor: 'testing' stamp: 'mt 10/1/2019 14:12'!
isFull

	^ self isNotifier not! !

!Debugger methodsFor: 'testing' stamp: 'mt 10/1/2019 14:12'!
isNotifier

	^ receiverInspector isNil! !


!Debugger class methodsFor: 'class initialization' stamp: 'mt 10/1/2019 17:56'!
initialize
	ContextStackKeystrokes := Dictionary new
		at: $e put: #send;
		at: $t put: #doStep;
		at: $T put: #stepIntoBlock;
		at: $p put: #proceed;
		at: $r put: #restart;
		at: $f put: #fullStack;
		at: $w put: #where;
		yourself.
	SavedExtent := self new initialExtent

	"Debugger initialize"! !

!Debugger class methodsFor: 'error recursion' stamp: 'mt 9/30/2019 17:50'!
clearErrorRecursion

	DebuggerErrorRecursionFlag value: false.! !

!Debugger class methodsFor: 'error recursion' stamp: 'mt 9/30/2019 17:50'!
isErrorRecursive

	^ DebuggerErrorRecursionFlag value! !

!Debugger class methodsFor: 'error recursion' stamp: 'mt 9/30/2019 17:50'!
setErrorRecursion

	DebuggerErrorRecursionFlag value: true.! !

!Debugger class methodsFor: '*60Deprecated-opening' stamp: 'mt 9/30/2019 17:28'!
openInterrupt: aString onProcess: aProcess

	self deprecated.
	^ aProcess debugWithTitle: aString full: false! !


!DebuggerErrorRecursionFlag class methodsFor: 'accessing' stamp: 'mt 9/30/2019 17:48'!
default

	^ false! !


!DebuggerTests methodsFor: 'tests' stamp: 'mt 10/1/2019 14:14'!
test01UserInterrupt
	
	| counter |
	{
		[ [counter := counter + 1] repeat ]. 
		[ [counter := counter + 1. true] whileTrue ]. 
		[ counter := counter + 1. Smalltalk createStackOverflow ].
	} do: [:workBlock |
		{
			Processor userSchedulingPriority.
			Processor userSchedulingPriority + 1.
			Processor userInterruptPriority
		} do: [:workPriority |
			counter := 0.
			self doTestUserInterruptFor: workBlock priority: workPriority.
			self assert: counter > 0.
			self assert: debugger isNotifier]].! !

!DebuggerTests methodsFor: 'tests' stamp: 'mt 10/1/2019 14:14'!
test02UnhandledException

	| counter |
	{
		[ counter := counter + 1. 7/0 ]. 'ZeroDivide'. 
		[ counter := counter + 1. Halt signal ]. 'Halt'.
		[ counter := counter + 1. Warning signal: 'Test' ]. 'Warning'
	} pairsDo: [:errorBlock :label |
		Processor systemBackgroundPriority
			to: Processor timingPriority
			by: 10
			do: [:priority |
				counter := 0.
	
				process := errorBlock forkAt: priority.
	
				"Let lower priority processes run to raise their exception."
				priority <= Processor activeProcess priority ifTrue: [
					3 timesRepeat: [process isSuspended not ifTrue: [50 milliSeconds wait]]].
				
				self ensureDebugger.
				
				self assert: counter > 0.
				self assert: (window label beginsWith: label).
				self assert: debugger isNotifier]]! !

!DebuggerTests methodsFor: 'tests' stamp: 'mt 10/1/2019 13:38'!
test03AsyncronousExceptions
	"We want to get n different debuggers from n different processes."
	
	((1 to: 3) "... more than 1 ... so ... 3 sounds about right ..."
		collect: [:inc | [ Error signal ] forkAt: Processor userSchedulingPriority + inc])
		do: [:errorProcess |
				"There must be debuggers for all processes."
				process := errorProcess.
				self ensureDebugger];
		in: [:all | debugger close "Just close the last debugger."];
		do: [:errorProcess |
				process := errorProcess.
				self findDebugger.
				
				self assert: debugger isNil.
				self assert: process isTerminated]! !

!DebuggerTests methodsFor: 'tests' stamp: 'mt 10/1/2019 14:58'!
test04DebuggerSuspendsProcess
	"Opening the debugger on a running process means to suspend that process."

	process := [ [3 + 4] repeat ] newProcess.
	
	"Choose a priority lower than the active process so that we can use an endless loop."
	process priority: Processor activeProcess priority - 1.
	process resume. Processor yield.

	self deny: process isSuspended.

	debugger := process debug.
	
	self assert: process isSuspended.! !

!DebuggerTests methodsFor: 'tests' stamp: 'mt 10/1/2019 14:58'!
test05DebuggerTerminatesProcess
	"Closing a debugger on a suspended process means terminating that process."
	
	process := [ [3 + 4] repeat ] newProcess.
	
	"Choose a priority lower than the active process so that we can use an endless loop."
	process priority: Processor activeProcess priority - 1.
	process resume. Processor yield.

	self deny: process isTerminated.

	debugger := process debug.
	debugger close.
	
	self assert: process isTerminated.! !

!DebuggerTests methodsFor: 'tests' stamp: 'mt 10/1/2019 14:58'!
test06DebugSpecificContext
	
	| context |

	process := [ #(1 2 3) collect: [:ea | ea odd ifTrue: [ ea ] ifFalse: [ Processor activeProcess suspend ] ] ] newProcess.
	process priority: Processor activeProcess priority + 1.
	process resume.

	"Find specific context by following #sender chain."
	context := process suspendedContext.
	[context selector = #collect: ] whileFalse: [context := context sender].

	debugger := process debug: context.

	self assert: debugger contextStackTop = context.! !

!DebuggerTests methodsFor: 'tests' stamp: 'mt 10/1/2019 14:57'!
test07DebuggerNotifier
	
	process := [ 3+4 ] newProcess.
	debugger := process debugWithTitle: 'Test' full: false.
	self assert: debugger isNotifier.! !

!DebuggerTests methodsFor: 'tests' stamp: 'mt 10/1/2019 14:57'!
test08DebuggerFull
	
	process := [ 3+4 ] newProcess.
	debugger := process debugWithTitle: 'Test' full: true.
	self assert: debugger isFull.! !

!DebuggerTests methodsFor: 'tests' stamp: 'mt 10/1/2019 14:57'!
test09DebuggerNotifierOrFull
	"Test the defaults."
		
	process := [ 3+4 ] newProcess.
	debugger := process debug.
	self assert: debugger isFull.
	
	debugger close.
	
	process := [ 3+4 ] newProcess.
	debugger := process debugWithTitle: 'Test'.
	self assert: debugger isFull.

	debugger close.
	
	process := [ 3+4 ] newProcess.
	debugger := process debug: process suspendedContext.
	self assert: debugger isNotifier.! !

!DebuggerTests methodsFor: 'tests' stamp: 'mt 10/1/2019 17:38'!
test10DebugBlock
	"Used for debug-it in tools."

	process := Process forBlock: [3+4].
	debugger := process debug.

	self deny: 7 equals: process suspendedContext top.
	debugger stepOver.
	self assert: 7 equals: process suspendedContext top.! !

!DebuggerTests methodsFor: 'tests' stamp: 'mt 10/1/2019 17:38'!
test11DebugBlockAtContext
	"Similar to the run-to feature in the debugger, run the process until a certain condition is met."
	
	process := Process
		forBlock: [(#(1 2 3) collect: [:ea | ea + 1]) in: [:all | all sum]]
		at: [:context | context selector = #sum].
	debugger := process debug.

	self deny: 9 equals: process suspendedContext top.
	debugger stepOver.
	self assert: 9 equals: process suspendedContext top.! !

!DebuggerTests methodsFor: 'support' stamp: 'mt 10/1/2019 13:54'!
doTestUserInterruptFor: block priority: priority
	"Note that we cannot signal the InterruptSemaphore because there is already the regular process waiting for it. We simulate that semaphore with a custom delay."
	
	| work wait interruptHelper |
	work := Semaphore new.
	wait := Semaphore new. "Maybe not necessary?"
	
	process := [ work wait. block value ] newProcess.
	process priority: priority.
	self assert: process priority >= Processor activeProcess priority. "You cannot interrupt lower priority processes."
	process resume.
	
	interruptHelper := [ wait wait. 10 milliSeconds wait. Smalltalk handleUserInterrupt ] newProcess.
	interruptHelper priority: Processor lowIOPriority.
	self assert: interruptHelper priority > process priority.
	interruptHelper resume.
	
	[wait signal. work signal] valueNoContextSwitch.
	process priority = Processor activeProcess priority ifTrue: [Processor yield].
	
	self assert: interruptHelper isTerminated.

	self ensureDebugger.! !

!DebuggerTests methodsFor: 'support' stamp: 'mt 10/1/2019 13:38'!
ensureDebugger

	debugger ifNotNil: [debugger close].
	
	debugger := nil.
	window := nil.
	
	self assert: process notNil.
	self assert: process isSuspended.

	self findDebugger.	
	
	self assert: window notNil.
	self assert: debugger notNil.! !

!DebuggerTests methodsFor: 'support' stamp: 'mt 10/1/2019 13:25'!
findDebugger

	window := nil.
	debugger := nil.
		
	self findDebuggerWindowFor: process.

	window ifNil: [
		self updateUserInterface.
		self findDebuggerWindowFor: process].

	window ifNotNil: [debugger := window model]! !

!DebuggerTests methodsFor: 'support' stamp: 'mt 10/1/2019 11:23'!
findDebuggerWindowFor: process

	window :=  (Project current world in: [:world | world submorphs, world firstHand submorphs])
		detect: [:m | (m isSystemWindow and: [m model isKindOf: Debugger]) and: [m model interruptedProcess == process]]
		ifNone: [].! !

!DebuggerTests methodsFor: 'support' stamp: 'mt 10/1/2019 10:17'!
updateUserInterface

	Project current world doOneCycle.! !

!DebuggerTests methodsFor: 'running' stamp: 'mt 10/1/2019 13:48'!
setUp

	super setUp.
	
	Project current isMorphic ifFalse: [self fail].
	Project current uiProcess isActiveProcess ifFalse: [self fail].
	
	Preferences disable: #logDebuggerStackToFile.! !

!DebuggerTests methodsFor: 'running' stamp: 'mt 10/1/2019 13:48'!
tearDown

	debugger ifNotNil: [debugger close].
	process ifNotNil: [process terminate].
	
	process := nil.
	debugger := nil.
	window := nil.
	
	Preferences enable: #logDebuggerStackToFile.
	
	super tearDown.! !


!DebuggerUnwindBug methodsFor: 'tests' stamp: 'mt 9/30/2019 17:31'!
testUnwindDebugger
	"test if unwind blocks work properly when a debugger is closed"
	| sema process debugger top |
	sema := Semaphore forMutualExclusion.
	self assert: sema isSignaled.
	process := [sema critical:[sema wait]] forkAt: Processor userInterruptPriority.
	self deny: sema isSignaled.

	"everything set up here - open a debug notifier"
	debugger := process debugWithTitle: 'test' full: false.
	"get into the debugger"
	debugger debug.
	top := debugger topView.
	"set top context"
	debugger toggleContextStackIndex: 1.
	"close debugger"
	top delete.

	"and see if unwind protection worked"
	self assert: sema isSignaled.! !


!MVCDebugger class methodsFor: 'opening' stamp: 'mt 9/30/2019 18:30'!
openOn: process context: context label: title contents: contentsStringOrNil fullView: full
	"Open a notifier in response to an error, halt, or notify. A notifier view just shows a short view of the sender stack and provides a menu that lets the user open a full debugger."

	| debugger |
	self isErrorRecursive ifTrue: [
		"self assert: process == Project current uiProcess -- DOCUMENTATION ONLY"
		self clearErrorRecursion.
		^ Project current handleFatalDrawingError: title].

	[self isErrorRecursive not & Preferences logDebuggerStackToFile
		ifTrue: [Smalltalk logSqueakError: title inContext: context]]
			on: Error
			do: [:ex | ex return: nil].
	
	self setErrorRecursion.
		
	self informExistingDebugger: context label: title.
		
	debugger := self new
		process: process
		controller: (Project current world activeControllerProcess == process
						ifTrue: [Project current world activeController])
		context: context.

	full
		ifTrue: [debugger openFullNoSuspendLabel: title]
		ifFalse: [debugger openNotifierNoSuspendContents: contentsStringOrNil label: title].

	"Try drawing the debugger tool at least once to avoid freeze."
	Project current restoreDisplay.
	
	self clearErrorRecursion.
	
	process suspend.	

	"If we are in a helper process, #openNoTerminate WILL NOT activate the debugger's controller."
	Project current world inActiveControllerProcess
		ifFalse: [Project current world searchForActiveController].
	
	"Get here only if active process is not the process-to-debug. Use helper process if you want to access this return value."
	^ debugger! !


!MenuItemMorph methodsFor: 'browse' stamp: 'mt 9/26/2019 18:08'!
browseImplementationOfActionSelector

	| method |
	method := target class lookupSelector: selector.
	ToolSet browse: method methodClass selector: method selector.! !

!MenuItemMorph methodsFor: 'browse' stamp: 'mt 9/27/2019 08:20'!
buildDebugMenu: aHandMorph
	| aMenu |
	aMenu := super buildDebugMenu: aHandMorph.
	aMenu addLine.
	aMenu add: 'browse action code' translated target: self action: #browseImplementationOfActionSelector.
	aMenu add: 'debug action invocation' translated target: self action: #debugAction.
	^ aMenu! !

!MenuItemMorph methodsFor: 'browse' stamp: 'mt 10/1/2019 17:05'!
debugAction

	(Process
		forBlock: [self doButtonAction]
		at: [:context | context selector = self selector])
			debugWithTitle: 'Debug menu action'.! !


!MorphicDebugger class methodsFor: 'opening' stamp: 'mt 10/1/2019 14:15'!
openOn: process context: context label: title contents: contentsStringOrNil fullView: full 
	"Open a notifier in response to an error, halt, or notify. A notifier view just shows a short view of the sender stack and provides a menu that lets the user open a full debugger."
	
	| errorWasInUIProcess debugger uiBlock |
	self isErrorRecursive ifTrue: [
		"self assert: process == Project current uiProcess -- DOCUMENTATION ONLY"
		self clearErrorRecursion.
		^ Project current handleFatalDrawingError: title].
	
	[self isErrorRecursive not & Preferences logDebuggerStackToFile
		ifTrue: [Smalltalk logSqueakError: title inContext: context]]
			on: Error
			do: [:ex | ex return: nil].
	
	errorWasInUIProcess := Project current spawnNewProcessIfThisIsUI: process.
	
	uiBlock :=  [
		self setErrorRecursion.
						
		self informExistingDebugger: context label: title.
		
		debugger := self new process: process context: context.
		full
			ifTrue: [debugger openFullNoSuspendLabel: title]
			ifFalse: [debugger openNotifierNoSuspendContents: contentsStringOrNil label: title].
		debugger errorWasInUIProcess: errorWasInUIProcess.
	
		"Try drawing the debugger tool at least once to avoid freeze."
		debugger topView ifNotNil: [:window | window world displayWorldSafely].
		
		self clearErrorRecursion].
		
	"Schedule debugging in a deferred UI message if necessary."
	(Project current uiProcess isActiveProcess not or: [process isActiveProcess])
		ifTrue: [Project current addDeferredUIMessage: uiBlock]
		ifFalse: uiBlock.
	
	process suspend.
	
	"Get here only if active process is not the process-to-debug. So in tests, use a helper process if you want to access this return value."
	^ debugger! !


!ParagraphEditor methodsFor: '*ST80Tools' stamp: 'mt 10/1/2019 17:26'!
debugIt

	| receiver context |
	self lineSelectAndEmptyCheck: [^self].

	(model respondsTo: #doItReceiver) 
		ifTrue: [receiver := model doItReceiver].
		
	(model respondsTo: #doItContext) 
		ifTrue: [context := model doItContext].
		
	(self compileSelectionFor: receiver in: context) ifNotNil: [:method |
		(Process forMethod: method receiver: receiver context: context)
			debugWithTitle: 'Debug it'].! !


!PluggableButtonMorph methodsFor: 'debug menu' stamp: 'mt 9/26/2019 17:52'!
browseImplementationOfActionSelector

	| method |
	method := model class lookupSelector: actionSelector.
	ToolSet browse: method methodClass selector: method selector.! !

!PluggableButtonMorph methodsFor: 'debug menu' stamp: 'mt 9/27/2019 08:20'!
buildDebugMenu: aHandMorph
	| aMenu |
	aMenu := super buildDebugMenu: aHandMorph.
	aMenu addLine.
	aMenu add: 'browse action code' translated target: self action: #browseImplementationOfActionSelector.
	aMenu add: 'debug action invocation' translated target: self action: #debugAction.
	^ aMenu! !

!PluggableButtonMorph methodsFor: 'debug menu' stamp: 'mt 10/1/2019 17:04'!
debugAction

	(Process
		forBlock: [self performAction]
		at: [:context | context selector = self actionSelector])
			debugWithTitle: 'Debug button action'.! !


!Process methodsFor: 'debugging' stamp: 'mt 9/30/2019 17:37'!
debug

	^ self debugWithTitle: 'Debug'! !

!Process methodsFor: 'debugging' stamp: 'mt 10/1/2019 14:05'!
debug: context

	^ self debug: context title: 'Debug'! !

!Process methodsFor: 'debugging' stamp: 'mt 9/30/2019 17:37'!
debug: context title: title
	"Open debugger on self with context shown on top"

	^ self debug: context title: title full: false
! !

!Process methodsFor: 'debugging' stamp: 'mt 9/30/2019 17:37'!
debug: context title: title full: bool

	^ self
		debug: context
		title: title
		full: bool
		contents: nil! !

!Process methodsFor: 'debugging' stamp: 'mt 9/30/2019 17:38'!
debug: context title: title full: bool contents: contents
	"Open debugger on self with context shown on top"

	| topCtxt |
	topCtxt := self isActiveProcess ifTrue: [thisContext] ifFalse: [self suspendedContext].
	(topCtxt hasContext: context) ifFalse: [^ self error: 'context not in process'].
	^ ToolSet debugProcess: self context: context label: title contents: contents fullView: bool! !

!Process methodsFor: 'debugging' stamp: 'mt 9/30/2019 17:38'!
debugWithTitle: title

	^ self debugWithTitle: title full: true! !

!Process methodsFor: 'debugging' stamp: 'mt 9/30/2019 17:38'!
debugWithTitle: title full: aBoolean

	^ self debugWithTitle: title full: aBoolean contents: nil! !

!Process methodsFor: 'debugging' stamp: 'mt 9/30/2019 17:38'!
debugWithTitle: title full: bool contents: contents
	"Automatically choose the top context."

	^ self
		debug: (self isActiveProcess ifTrue: [thisContext] ifFalse: [self suspendedContext])
		title: title
		full: bool
		contents: contents! !


!Process class methodsFor: 'instance creation' stamp: 'mt 10/1/2019 17:33'!
forBlock: aBlock

	^ self
		forBlock: aBlock
		at: [:context | context method == aBlock method ]! !

!Process class methodsFor: 'instance creation' stamp: 'mt 10/1/2019 17:02'!
forBlock: aBlock at: aConditionBlock

	| guineaPig |
	guineaPig := aBlock newProcess.

	"Step until the condition is fulfilled."
	[
		(aConditionBlock isNil
			or: [guineaPig isTerminated])
			or: [aConditionBlock value: guineaPig suspendedContext]
	] whileFalse: [guineaPig step].

	guineaPig stepToSendOrReturn.
	
	^ guineaPig! !

!Process class methodsFor: 'instance creation' stamp: 'mt 10/1/2019 17:34'!
forMethod: method
receiver: anObject
context: aContextOrNil

	^ self
		forMethod: method
		receiver: anObject
		context: aContextOrNil
		at: [:context |
			"Now step into the expression.  But if it is quick (is implemented as a primtiive, e.g. `0') it will return immediately back to the block that is sent newProcess above.  Guard against that with the check for home being thisContext."
			context method == method]! !

!Process class methodsFor: 'instance creation' stamp: 'mt 10/1/2019 17:35'!
forMethod: method
receiver: anObject
context: aContextOrNil
at: aConditionBlock

	^ self
		forBlock: [
			method
				valueWithReceiver: anObject
				arguments: (aContextOrNil ifNil: [#()] ifNotNil: [{aContextOrNil}])]
		at: aConditionBlock! !


!Project methodsFor: 'scheduling & debugging' stamp: 'mt 10/1/2019 18:19'!
interruptName: labelString
	"Create a Notifier on the active scheduling process with the given label."

	^ self
		interruptName: labelString
		message: nil
		preemptedProcess: nil! !

!Project methodsFor: 'scheduling & debugging' stamp: 'mt 10/1/2019 18:19'!
interruptName: labelString message: aMessage
	"Create a Notifier on the active scheduling process with the given label."

	^ self
		interruptName: labelString
		message: aMessage
		preemptedProcess: nil! !

!Project methodsFor: 'scheduling & debugging' stamp: 'mt 10/1/2019 18:18'!
interruptName: labelString message: aMessage preemptedProcess: theInterruptedProcess
	"Create a Notifier on the active scheduling process with the given label."

	| preemptedProcess projectProcess |
	projectProcess := self uiProcess.
	preemptedProcess := theInterruptedProcess ifNil: [Processor preemptedProcess].
	
	"Only debug preempted process if its priority is >= projectProcess' priority"
	preemptedProcess priority < projectProcess priority 
		ifTrue:[preemptedProcess := projectProcess].

	"Give projects a change to clean up."
	self interruptCleanUpFor: preemptedProcess.

	^ preemptedProcess debugWithTitle: labelString full: false contents: aMessage! !

!Project methodsFor: 'scheduling & debugging' stamp: 'mt 10/1/2019 18:19'!
interruptName: labelString preemptedProcess: theInterruptedProcess
	
	self
		interruptName: labelString
		message: nil
		preemptedProcess: theInterruptedProcess.! !


!MVCProject methodsFor: 'scheduling & debugging' stamp: 'mt 10/1/2019 18:08'!
interruptCleanUpFor: interruptedProcess

	super interruptCleanUpFor: interruptedProcess.
	
	(world activeController ~~ nil and: [world activeController ~~ world screenController]) ifTrue: [
		interruptedProcess == self uiProcess
			ifTrue: [
				world activeController view topView deEmphasizeForDebugger]
			ifFalse: [
				world activeController controlTerminate]].! !


!MorphicProject methodsFor: 'scheduling & debugging' stamp: 'mt 10/1/2019 18:05'!
interruptCleanUpFor: interruptedProcess
	"Clean up things in case of a process interrupt."

	super interruptCleanUpFor: interruptedProcess.

	self uiProcess == interruptedProcess ifTrue: [
		ActiveHand ifNotNil: [ActiveHand interrupted].
		ActiveWorld := world. "reinstall active globals"
		ActiveHand := world primaryHand.
		ActiveHand interrupted. "make sure this one's interrupted too"
		ActiveEvent := nil.
		
		Preferences eToyFriendly
			ifTrue: [Project current world stopRunningAll]].! !


!Project class methodsFor: '*Etoys-Squeakland-utilities' stamp: 'mt 9/30/2019 17:22'!
interruptName: labelString preemptedProcess: theInterruptedProcess

	^ Project current interruptName: labelString preemptedProcess: theInterruptedProcess! !


!SmalltalkImage methodsFor: 'memory space' stamp: 'mt 9/30/2019 17:39'!
lowSpaceChoices
	"Return a notifier message string to be presented when space is running low."

	^ 'Warning!! Squeak is almost out of memory!!

Low space detection is now disabled. It will be restored when you close or proceed from this error notifier. Don''t panic, but do proceed with caution.

Here are some suggestions:

 If you suspect an infinite recursion (the same methods calling each other again and again), then close this debugger, and fix the problem.

 If you want this computation to finish, then make more space available (read on) and choose "proceed" in this debugger. Here are some ways to make more space available...
   > Close any windows that are not needed.
   > Get rid of some large objects (e.g., images).
   > Leave this window on the screen, choose "save as..." from the screen menu, quit, restart the Squeak VM with a larger memory allocation, then restart the image you just saved, and choose "proceed" in this window.

 If you want to investigate further, choose "debug" in this window.  Do not use the debugger "fullStack" command unless you are certain that the stack is not very deep. (Trying to show the full stack will definitely use up all remaining memory if the low-space problem is caused by an infinite recursion!!).

'
! !

!SmalltalkImage methodsFor: 'memory space' stamp: 'mt 10/1/2019 18:18'!
lowSpaceWatcher
	"Wait until the low space semaphore is signalled, then take appropriate actions."

	| free preemptedProcess |
	self garbageCollectMost <= self lowSpaceThreshold
		ifTrue: [self garbageCollect <= self lowSpaceThreshold
				ifTrue: ["free space must be above threshold before
					starting low space watcher"
					^ Beeper beep]].

	Smalltalk specialObjectsArray at: 23 put: nil.  "process causing low space will be saved here"
	LowSpaceSemaphore := Semaphore new.
	self primLowSpaceSemaphore: LowSpaceSemaphore.
	self primSignalAtBytesLeft: self lowSpaceThreshold.  "enable low space interrupts"

	LowSpaceSemaphore wait.  "wait for a low space condition..."

	self primSignalAtBytesLeft: 0.  "disable low space interrupts"
	self primLowSpaceSemaphore: nil.
	LowSpaceProcess := nil.

	"The process that was active at the time of the low space interrupt."
	preemptedProcess := Smalltalk specialObjectsArray at: 23.
	Smalltalk specialObjectsArray at: 23 put: nil.

	"Note: user now unprotected until the low space watcher is re-installed"

	self memoryHogs isEmpty
		ifFalse: [free := self bytesLeft.
			self memoryHogs
				do: [ :hog | hog freeSomeSpace ].
			self bytesLeft > free
				ifTrue: [ ^ self installLowSpaceWatcher ]].

	Preferences logDebuggerStackToFile ifTrue: [
		self
			logError: 'Space is low'
			inContext: preemptedProcess suspendedContext
			to: 'LowSpaceDebug.log'].
			
	Project current
			interruptName: 'Space is low'
			message: self lowSpaceChoices
			preemptedProcess: preemptedProcess
! !


!StandardToolSet class methodsFor: 'debugging - handlers' stamp: 'mt 9/30/2019 17:43'!
handleUserInterruptRequest: aString
	"Double dispatch. Let the current project manage processes, which usually calls back here eventually into #debugProcess..."
	
	^ Project current interruptName: aString! !

!StandardToolSet class methodsFor: 'debugging' stamp: 'mt 10/1/2019 16:45'!
debugProcess: aProcess context: aContext label: aString contents: contents fullView: aBool

	(aProcess isTerminated and: [aString beginsWith: 'Debug it']) ifTrue: [
		^ Project uiManager inform: 'Nothing to debug. Process has terminated.\Expression optimized.' withCRs].

	^ Debugger
		openOn: aProcess
		context: aContext
		label: aString
		contents: contents
		fullView: aBool! !

!StandardToolSet class methodsFor: '*60Deprecated-debugging' stamp: 'mt 9/30/2019 17:16'!
interrupt: aProcess label: aString

	self deprecated.
	aProcess debugWithTitle: aString full: false.! !


!TextEditor methodsFor: 'do-its' stamp: 'mt 10/1/2019 17:24'!
debugIt

	| receiver context |
	self lineSelectAndEmptyCheck: [^self].

	(model respondsTo: #debugExpression:)
		ifTrue: [^ model perform: #debugExpression: with: self selection].

	receiver := (model respondsTo: #doItReceiver) 
		ifTrue: [model doItReceiver]
		ifFalse: [nil].
		
	context := (model respondsTo: #doItContext) 
		ifTrue: [model doItContext]
		ifFalse: [nil].

	(self compileSelectionFor: receiver in: context) ifNotNil: [:method |
		(Process forMethod: method receiver: receiver context: context)
			debugWithTitle: 'Debug it'].! !


!ToolSet class methodsFor: '*60Deprecated-debugging' stamp: 'mt 10/1/2019 17:21'!
debugActiveProcessContext: aContext label: aString contents: contents

	self deprecated.
	^ self
		debugProcess: Processor activeProcess
		context: aContext
		label: aString
		contents: contents
		fullView: false! !

!ToolSet class methodsFor: '*60Deprecated-debugging' stamp: 'mt 10/1/2019 17:20'!
debugContext: aContext label: aString contents: contents

	self deprecated.
	^  self
		debugProcess: Processor activeProcess
		context: aContext
		label: aString
		contents: contents
		fullView: false! !

!ToolSet class methodsFor: '*60Deprecated-debugging' stamp: 'mt 10/1/2019 17:16'!
debugMethod: aCompiledMethod forReceiver: anObject inContext: aContextOrNil

	self deprecated.
	
	^ (Process
		forMethod: aCompiledMethod
		receiver: anObject
		context: aContextOrNil)
			debugWithTitle: 'Debug it'! !

!ToolSet class methodsFor: '*60Deprecated-debugging' stamp: 'mt 9/30/2019 17:15'!
interrupt: aProcess label: aString

	self deprecated.
	aProcess debugWithTitle: aString full: false.! !

ToolSet class removeSelector: #debugInterruptedProcess:label:!

!ToolSet class reorganize!
('accessing' askForDefault)
('browsing' browse:selector: browseChangeSetsWithClass:selector: browseClass: browseClass:category: browseHierarchy:selector: browseMessageCategory:inClass: browseMessageNames: browseMessageSet:name:autoSelect: browseVersionsOf:selector: openChangedMessageSet: openClassListBrowser:title:)
('*60Deprecated-debugging' debug:context:label:contents:fullView: debugActiveProcessContext:label:contents: debugContext:label:contents: debugError: debugMethod:forReceiver:inContext: interrupt:label:)
('debugging - handlers' handleError: handleSyntaxError: handleUserInterruptRequest: handleWarning:)
('debugging' debugProcess:context:label:contents:fullView: debugSyntaxError:)
('inspecting' basicInspect: explore: explore:label: inspect: inspect:label:)
('*60Deprecated-inspecting' inspectorClassOf:)
('menu' menuItems)
('completion' codeCompletionAround:textMorph:keyStroke:)
!


!TextEditor reorganize!
('menu messages' accept again againUpToEnd align browseChangeSetsWithSelector browseClassFromIt browseIt browseItHere cancel changeAlignment chooseAlignment classCommentsContainingIt classNamesContainingIt compareToClipboard copySelection cut exchange explain fileItIn find findAgain findReplace findReplaceAgain implementorsOfIt methodNamesContainingIt methodSourceContainingIt methodStringsContainingit paste pasteRecent prettyPrint prettyPrint: prettyPrintWithColor printerSetup redo referencesToIt saveContentsInFile selectedSelector selectedSymbol sendContentsToPrinterWithLabel: sendersOfIt setAlignment: setSearchString spawn undo)
('new selection' afterSelectionInsertAndSelect: correctFrom:to:with: encompassLine: insertAndSelect:at: lineSelectAndEmptyCheck: nextTokenFrom:direction: notify:at:in: selectFrom:to: selectLine selectPrecedingIdentifier)
('private' applyAttribute: beginningOfLine: endOfLine: exchangeWith: existingIndentation indent:fromStream:toStream: indexOfNextNonwhitespaceCharacter isDisjointFrom: moveCursor:forward:event:specialBlock: nullText pageHeight resetTypeAhead sameColumn:newLine:forward: typeAhead unapplyAttribute:)
('editing keys' abandonChangeText align: browseIt: browseItHere: cancel: changeEmphasis: changeLfToCr: chooseColor compareToClipboard: copyHiddenInfo debugIt: doIt: emphasisExtras enclose: exchange: exploreIt: fileItIn: handleEmphasisExtra:with: hiddenInfo implementorsOfIt: inOutdent:delta: indent: inspectIt: makeCapitalized: makeLowercase: makeUppercase: methodNamesContainingIt: methodStringsContainingIt: offerFontMenu: outdent: pasteInitials: printIt: redo: referencesToIt: save: sendersOfIt: setEmphasis: spawnIt: swapChars: undo:)
('typing/selecting keys' argAdvance: changeStyle: crWithIndent: destructiveBackWord: displayIfFalse: displayIfTrue: doAgain: doAgainUpToEnd: find: findAgain: findReplace: findReplaceAgain: forwardDelete: querySymbol: tabCount)
('typing support' addString: autoEncloseFor: backTo: closeTypeIn dispatchOnEnterWith: dispatchOnKeyboardEvent: findAgainNow findReplaceAgainNow hasMultipleLinesSelected insertAndCloseTypeIn insertTypeAhead isTypingIn openTypeIn openTypeInFor: removePreviousInsertion setEmphasisHere setEmphasisHereFromText setEmphasisHereFromTextForward: startOfTyping)
('binding' bindingOf:)
('parenblinking' blinkParen blinkParenAt: blinkPrevParen: clearParens)
('attributes' changeEmphasisOrAlignment changeSelectionFontTo: changeStyle changeTextFont offerFontMenu)
('initialize-release' changeParagraph: resetState stateArray stateArrayPut:)
('do-its' compileSelectionFor:in: debugIt doIt evaluateSelection evaluateSelectionAndDo: exploreIt inspectIt printIt)
('nonediting/nontyping keys' cursorEnd: cursorHome: raiseContextMenu: selectCurrentTypeIn:)
('explain' explainAnySel: explainChar: explainClass: explainCtxt: explainDelimitor: explainGlobal: explainInst: explainMySel: explainNumber: explainPartSel: explainScan: explainTemp:)
('displaying' flash)
('accessing-selection' hasCaret markIndex markIndex:pointIndex: pointBlock pointIndex selection selectionAsStream startBlock startIndex stopBlock stopIndex unselect)
('model access' model:)
('editing dragdrop' addText:event:)
('events' click: isEventInSelection: keyStroke: mouseDown: mouseMove: mouseUp: updateCursorForEvent: yellowButtonDown:)
('menu commands' offerMenuFromEsc:)
('current selection' recomputeSelection reverseSelection selectAndScroll)
('accessing' history history: model paragraph setReplacementFromHistory setSearch: setSearchFromSelectionOrHistory string styler styler: text totalTextHeight transformFrom: visibleHeight)
('scrolling' scrollBy: updateMarker)
('mvc compatibility' storeSelectionInParagraph zapSelectionWith:)
('undo' redoAndReselect replace:with: replace:with:and: replaceSelectionWith: undoAndReselect)
('as yet unclassified' systemNavigation)
!

CommandLineToolSet class removeSelector: #debugMethod:forReceiver:inContext:!
StandardToolSet class removeSelector: #debugInterruptedProcess:label:!
StandardToolSet class removeSelector: #debugMethod:forReceiver:inContext:!
MorphicProject removeSelector: #interruptName:!
MorphicProject removeSelector: #interruptName:preemptedProcess:!
MVCProject removeSelector: #interruptName:!
MVCProject removeSelector: #interruptName:preemptedProcess:!

!ParagraphEditor reorganize!
('accessing' initialText replace:with:and: replaceSelectionWith: setSearch: text totalTextHeight userHasEdited visibleHeight)
('accessing-selection' charBefore hasCaret hasSelection mark markBlock markBlock: markIndex pointBlock pointBlock: pointIndex selection selectionAsStream selectionInterval setMark: setPoint: startBlock startBlock: startIndex stopBlock stopBlock: stopIndex unselect wasComposition zapSelectionWith: zapSelectionWithCompositionWith:)
('as yet unclassified' activateTextActions)
('controlling' controlInitialize controlTerminate normalActivity)
('current selection' deselect initializeSelection recomputeInterval recomputeSelection reverseSelection select selectAndScroll selectAndScrollToTop)
('displaying' display flash)
('do-its' compileSelectionFor:in: doIt evaluateSelection evaluateSelectionAndDo: exploreIt inspectIt objectsReferencingIt printIt tallyIt tallySelection)
('editing keys' abandonChangeText align: browseIt: browseItHere: cancel: changeEmphasis: changeLfToCr: chooseColor compareToClipboard: copyHiddenInfo copySelection: cut: debugIt: doIt: enclose: exchange: exploreIt: fileItIn: hiddenInfo implementorsOfIt: inOutdent:delta: indent: inspectIt: makeCapitalized: makeLowercase: makeProjectLink: makeUppercase: methodNamesContainingIt: methodStringsContainingIt: noop: offerFontMenu offerFontMenu: outdent: paste: pasteInitials: printIt: referencesToIt: save: sendersOfIt: setEmphasis: spawnIt: swapChars: tempCommand: undo:)
('explain' explainAnySel: explainChar: explainClass: explainCtxt: explainGlobal: explainInst: explainMySel: explainNumber: explainPartSel: explainScan: explainTemp:)
('initialize-release' changeParagraph: resetState stateArray stateArrayPut:)
('menu messages' accept again align browseClassFromIt browseIt cancel changeAlignment changeEmphasis changeEmphasisOrAlignment changeStyle changeStyleTo: chooseAlignment classCommentsContainingIt classNamesContainingIt clipboardText clipboardText: clipboardTextPut: compareToClipboard copySelection cut exchange experimentalCommand explain fileItIn find findAgain fit implementorsOfIt lineSelectAndEmptyCheck: makeProjectLink methodNamesContainingIt methodSourceContainingIt methodStringsContainingit mvcRedisplay offerMenuFromEsc: paste pasteRecent performMenuMessage: presentSpecialMenu prettyPrint prettyPrint: prettyPrintWithColor printerSetup referencesToIt saveContentsInFile selectedSelector selectedSymbol sendContentsToPrinter sendersOfIt setAlignment: setSearchString spawn spawnWorkspace specialMenuItems undo)
('new selection' adjustSelection: afterSelectionInsertAndSelect: computeIntervalFrom:to: correctFrom:to:with: encompassLine: insertAndSelect:at: nextTokenFrom:direction: notify:at:in: selectAt: selectFrom:to: selectInterval: selectInvisiblyFrom:to: selectInvisiblyMark:point: selectLine selectMark:point: selectPrecedingIdentifier selectWord)
('nonediting/nontyping keys' comment cursorDown: cursorEnd: cursorHome: cursorLeft: cursorPageDown: cursorPageUp: cursorRight: cursorUp: escapeToDesktop: raiseContextMenu: selectCurrentTypeIn: selectWord: setSearchString:)
('parenblinking' blinkParen blinkParenAt: blinkPrevParen blinkPrevParen: clearParens dispatchOnCharacter:with:)
('scrolling' computeMarkerRegion markerDelta scrollAmount scrollBar scrollBy: scrollRectangleHeight scrollToBottom scrollToTop scrollView: updateMarker viewDelta)
('sensor access' processBlueButton processKeyboard processMouseButtons processRedButton processYellowButton)
('typing support' backTo: closeTypeIn closeTypeIn: dispatchOnEnterWith: doneTyping insertTypeAhead: openTypeIn readKeyboard setEmphasisHere simulatedKeystroke: startOfTyping)
('typing/selecting keys' argAdvance: backWord: backspace: changeStyle: crWithIndent: cursorTopHome: displayIfFalse: displayIfTrue: doAgainMany: doAgainOnce: find: findAgain: forwardDelete: normalCharacter: querySymbol: search: selectAll selectAll: simulatedBackspace tabOrIndent:)
('undo support' isDoing isRedoing isUndoing noUndoer undoMessage:forRedo: undoer: undoer:with: undoer:with:with: undoer:with:with:with:)
('undoers' undoAgain:andReselect:typedKey: undoAndReselect:redoAndReselect: undoCutCopy: undoQuery:lastOffering: undoReplace)
('private' againOnce: againOrSame: againOrSame:many: completeSymbol:lastOffering: exchangeWith: explainDelimitor: getPluggableYellowButtonMenu: isDisjointFrom: lines moveCursor:forward:specialBlock: nextWord: nullText pageHeight previousWord: sameColumn:newLine:forward: setIndices:forward:)
('*ST80Tools' browseChangeSetsWithSelector browseItHere debugIt)
('*Etoys-Squeakland-private' indent:fromStream:toStream:)
('*Etoys-Squeakland-typing support' recognizeCharacters recognizeCharactersWhileMouseIn:)
('*Etoys-Squeakland-editing keys' recognizer: shiftEnclose:)
!

MorphicDebugger class removeSelector: #openInterrupt:onProcess:!
MorphicDebugger class removeSelector: #openOnMethod:forReceiver:inContext:!
MVCDebugger class removeSelector: #openInterrupt:onProcess:!
MVCDebugger class removeSelector: #openOnMethod:forReceiver:inContext:!
DebuggerTests removeSelector: #expectedFailures!
DebuggerTests removeSelector: #test02UnhandledError!
DebuggerTests removeSelector: #test10DebugExpression!
DebuggerTests removeSelector: #test10DebugIt!
DebuggerTests removeSelector: #test11DebugExpressionForReceiver!
DebuggerTests removeSelector: #test12DebugExpressionWithBindings!
DebuggerTests removeSelector: #test13DebugExpressionAtContext!
DebuggerTests removeSelector: #test13DebugExpressionRunTo!
DebuggerTests removeSelector: #testUserInterruptFor:!
DebuggerTests removeSelector: #testUserInterruptFor:priority:!
DebuggerTests removeSelector: #updateVariables!

!DebuggerTests reorganize!
('tests' test01UserInterrupt test02UnhandledException test03AsyncronousExceptions test04DebuggerSuspendsProcess test05DebuggerTerminatesProcess test06DebugSpecificContext test07DebuggerNotifier test08DebuggerFull test09DebuggerNotifierOrFull test10DebugBlock test11DebugBlockAtContext)
('support' doTestUserInterruptFor:priority: ensureDebugger findDebugger findDebuggerWindowFor: updateUserInterface)
('running' setUp tearDown)
!


!DebuggerErrorRecursionFlag class reorganize!
('accessing' default)
!

Debugger initialize!
Debugger class removeSelector: #lowSpaceChoices!
Debugger class removeSelector: #openOnMethod:forReceiver:inContext:!

!Debugger class reorganize!
('class initialization' initialize)
('instance creation' informExistingDebugger:label:)
('opening' openOn:context:label:contents:fullView:)
('error recursion' clearErrorRecursion isErrorRecursive setErrorRecursion)
('private' ifPreferredInterruptUIProcessIfBlocked:)
('preferences' errorReportServer errorReportServer: fullStackSize fullStackSize: interruptUIProcessIfBlockedOnErrorInBackgroundProcess interruptUIProcessIfBlockedOnErrorInBackgroundProcess: notifierStackSize notifierStackSize: stackSizeLimit stackSizeLimit: wantsAnnotationPane wantsAnnotationPane:)
('*60Deprecated-opening' context: openContext:label:contents: openInterrupt:onProcess:)
!

Debugger removeSelector: #showPCRange!
CodeHolder subclass: #Debugger
	instanceVariableNames: 'interruptedProcess contextStack contextStackIndex contextStackList receiverInspector contextVariablesInspector externalInterrupt proceedValue selectingPC savedCursor isolationHead failedProject labelString message untilExpression'
	classVariableNames: 'ContextStackKeystrokes ErrorReportServer FullStackSize InterruptUIProcessIfBlockedOnErrorInBackgroundProcess NotifierStackSize SavedExtent StackSizeLimit WantsAnnotationPane'
	poolDictionaries: ''
	category: 'Tools-Debugger'!
ControlManager removeSelector: #interruptName:!
ControlManager removeSelector: #interruptName:preemptedProcess:!
