<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto">Hi,<div><br></div><div>regarding</div><div><br></div><div><blockquote type="cite"><font color="#000000"><span style="caret-color: rgb(0, 0, 0); background-color: rgba(255, 255, 255, 0);">+    obj := parents at: obj ifAbsent: [nil].<br>-    obj := parents at: obj ifAbsent: [].</span></font></blockquote><div><br></div>Please no.  One must know that the empty block evaluates to nil.  It is illiterate not to.  So the verbosity is bad; it implies uncertainty (“does the empty block evaluate to nil?  maybe not ‘cuz here it’s written explicitly”), it requires more typing, it’s ugly.  </div><div><br></div><div>Another thing one should know is that </div><div><span style="background-color: rgba(255, 255, 255, 0);">    e ifTrue: [s]</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">is the same as</span></div><div>    e ifTrue: [s] ifFalse: []</div><div>etc. ie, if e is false the value is nil.</div><div><br></div><div><br><div id="AppleMailSignature"><span style="background-color: rgba(255, 255, 255, 0);">_,,,^..^,,,_ (phone)</span></div><div><br>On Aug 13, 2018, at 3:25 AM, <a href="mailto:commits@source.squeak.org">commits@source.squeak.org</a> wrote:<br><br></div><blockquote type="cite"><div><span>A new version of Tools was added to project The Inbox:</span><br><span><a href="http://source.squeak.org/inbox/Tools-LM.828.mcz">http://source.squeak.org/inbox/Tools-LM.828.mcz</a></span><br><span></span><br><span>==================== Summary ====================</span><br><span></span><br><span>Name: Tools-LM.828</span><br><span>Author: LM</span><br><span>Time: 13 August 2018, 1:25:54.504807 pm</span><br><span>UUID: 061511ca-729a-ce43-a08c-8057c24f7406</span><br><span>Ancestors: Tools-tcj.827</span><br><span></span><br><span>Added the ability to exclude specific objects from the  PointerFinder.</span><br><span>Improved the Explorer's "chase pointers" context menu to exclude the Explorer itself from the search (includes some meta-programming, not ideal, but certainly better than previously, ideas for improvement appreciated).</span><br><span>Changed the way the PointerExplorer displays references, it now states the associations name on the left, and the Objects hash is moved to the right, together with the objects displayString.</span><br><span>These changes make it much easier to understand how the objects are associated with each other and should make it easier to track down memory leaks.</span><br><span></span><br><span>=============== Diff against Tools-tcj.827 ===============</span><br><span></span><br><span>Item was changed:</span><br><span>  ----- Method: Inspector>>chasePointers (in category 'menu commands') -----</span><br><span>  chasePointers</span><br><span>      | selected  saved |</span><br><span>      self selectionIndex = 0 ifTrue: [^ self changed: #flash].</span><br><span>      selected := self selection.</span><br><span>      saved := self object.</span><br><span>      [self object: nil.</span><br><span>      (Smalltalk includesKey: #PointerFinder)</span><br><span>          ifTrue: [PointerFinder on: selected]</span><br><span>          ifFalse: [self inspectPointers]]</span><br><span>          ensure: [self object: saved]!</span><br><span></span><br><span>Item was changed:</span><br><span>  ----- Method: ObjectExplorer>>chasePointersForSelection (in category 'menus - actions') -----</span><br><span>  chasePointersForSelection</span><br><span>      </span><br><span>+     PointerFinder on: self object except: {self}, ObjectExplorerWrapper allInstances!</span><br><span>-     self flag: #tooMany. "mt: Note that we might want to ignore references caused by this tool."</span><br><span>-     self object chasePointers.!</span><br><span></span><br><span>Item was changed:</span><br><span>  ----- Method: PointerExplorer>>rootObject: (in category 'accessing') -----</span><br><span>  rootObject: anObject</span><br><span></span><br><span>+     self root key: 'root'.</span><br><span>-     self root key: anObject identityHash asString.</span><br><span>      super rootObject: anObject.!</span><br><span></span><br><span>Item was changed:</span><br><span>  ----- Method: PointerExplorerWrapper>>contents (in category 'accessing') -----</span><br><span>  contents</span><br><span>      "Return the wrappers with the objects holding references to item. Eldest objects come first, weak only referencers are at the end and have parentheses around their identity hash."</span><br><span></span><br><span>      | objects weakOnlyReferences |</span><br><span>      objects := self object inboundPointersExcluding: { self. self item. model }.</span><br><span>      weakOnlyReferences := OrderedCollection new.</span><br><span>      objects removeAllSuchThat: [ :each |</span><br><span>          each class == self class </span><br><span>              or: [ each class == PointerExplorer</span><br><span>              or: [ (each isContext</span><br><span>                  and: [ (each objectClass: each receiver) == PointerExplorer ] )</span><br><span>              or: [ (each pointsOnlyWeaklyTo: self object)</span><br><span>                  ifTrue: [ weakOnlyReferences add: each. true ]</span><br><span>                  ifFalse: [ false ] ] ] ] ].</span><br><span>       ^(objects replace: [ :each |    </span><br><span>+         self class with: each name: (self nameForParent: each) model: self object ])</span><br><span>-         self class with: each name: each identityHash asString model: self object ])</span><br><span>          addAll: (weakOnlyReferences replace: [ :each |</span><br><span>+             (self class with: each name: '(', (self nameForParent: each), ')' model: self object)</span><br><span>-             (self class with: each name: '(', each identityHash asString, ')' model: self object)</span><br><span>                  weakOnly: true;</span><br><span>                  yourself ]);</span><br><span>          yourself!</span><br><span></span><br><span>Item was added:</span><br><span>+ ----- Method: PointerExplorerWrapper>>explorerStringFor: (in category 'converting') -----</span><br><span>+ explorerStringFor: anObject</span><br><span>+ </span><br><span>+     ^ anObject identityHash asString, ': ', (super explorerStringFor: anObject).!</span><br><span></span><br><span>Item was added:</span><br><span>+ ----- Method: PointerExplorerWrapper>>memberNameFrom:to: (in category 'accessing') -----</span><br><span>+ memberNameFrom: aParent to: aChild</span><br><span>+ </span><br><span>+     1 to: aParent class instSize do: [ :instVarIndex |</span><br><span>+         (aParent instVarAt: instVarIndex) = aChild</span><br><span>+             ifTrue: [ ^ '#', (aParent class instVarNameForIndex: instVarIndex)]].</span><br><span>+     "This also covers arrays"</span><br><span>+     1 to: aParent basicSize do: [ :index |</span><br><span>+         (aParent basicAt: index) = aChild</span><br><span>+             ifTrue: [^ index asString]].</span><br><span>+     ^ '???'!</span><br><span></span><br><span>Item was added:</span><br><span>+ ----- Method: PointerExplorerWrapper>>nameForParent: (in category 'accessing') -----</span><br><span>+ nameForParent: anObject</span><br><span>+ </span><br><span>+     ^  self memberNameFrom: anObject to: self object!</span><br><span></span><br><span>Item was changed:</span><br><span>  Model subclass: #PointerFinder</span><br><span>+     instanceVariableNames: 'goal parents toDo toDoNext hasGemStone pointerList objectList parentsSize todoSize depth pointerListIndex excludedObjects'</span><br><span>-     instanceVariableNames: 'goal parents toDo toDoNext hasGemStone pointerList objectList parentsSize todoSize depth pointerListIndex'</span><br><span>      classVariableNames: ''</span><br><span>      poolDictionaries: ''</span><br><span>      category: 'Tools-Debugger'!</span><br><span></span><br><span>  !PointerFinder commentStamp: '<historical>' prior: 0!</span><br><span>  I can search for reasons why a certain object isn't garbage collected.  I'm a quick port of a VisualWorks program written by Hans-Martin Mosner.  Call me as shown below.  I'll search for a path from a global variable to the given object, presenting it in a small morphic UI.</span><br><span></span><br><span>  Examples:</span><br><span>      PointerFinder on: self currentHand</span><br><span>      PointerFinder on: StandardSystemView someInstance</span><br><span></span><br><span>  Now, let's see why this image contains more HandMorphs as expected...</span><br><span></span><br><span>  HandMorph allInstancesDo: [:e | PointerFinder on: e]!</span><br><span></span><br><span>Item was added:</span><br><span>+ ----- Method: PointerFinder class>>on:except: (in category 'instance creation') -----</span><br><span>+ on: anObject except: aCollection</span><br><span>+     ^ self new </span><br><span>+         goal: anObject;</span><br><span>+         excludedObjects: aCollection;</span><br><span>+         search;</span><br><span>+         open!</span><br><span></span><br><span>Item was changed:</span><br><span>  ----- Method: PointerFinder>>buildList (in category 'application') -----</span><br><span>  buildList</span><br><span>      | list obj parent object key |</span><br><span>      list := OrderedCollection new.</span><br><span>      obj := goal.</span><br><span>      </span><br><span>      [list addFirst: obj.</span><br><span>+     obj := parents at: obj ifAbsent: [nil].</span><br><span>-     obj := parents at: obj ifAbsent: [].</span><br><span>      obj == nil] whileFalse.</span><br><span>      list removeFirst.</span><br><span>      parent := Smalltalk.</span><br><span>      objectList := OrderedCollection new.</span><br><span>      pointerList := OrderedCollection new.</span><br><span>      [list isEmpty]</span><br><span>          whileFalse: </span><br><span>              [object := list removeFirst.</span><br><span>              key := nil.</span><br><span>              (parent isKindOf: Dictionary)</span><br><span>                  ifTrue: [list size >= 2</span><br><span>                          ifTrue: </span><br><span>                              [key := parent keyAtValue: list second ifAbsent: [].</span><br><span>                              key == nil</span><br><span>                                  ifFalse: </span><br><span>                                      [object := list removeFirst; removeFirst.</span><br><span>                                      pointerList add: key printString , ' -> ' , object class name]]].</span><br><span>              key == nil</span><br><span>                  ifTrue: </span><br><span>                      [parent class == object ifTrue: [key := 'CLASS'].</span><br><span>                      key == nil ifTrue: [1 to: parent class instSize do: [:i | key == nil ifTrue: [(parent instVarAt: i)</span><br><span>                                      == object ifTrue: [key := parent class instVarNameForIndex: i]]]].</span><br><span>                      key == nil ifTrue: [parent isCompiledCode ifTrue: [key := 'literals?']].</span><br><span>                      key == nil ifTrue: [1 to: parent basicSize do: [:i | key == nil ifTrue: [(parent basicAt: i)</span><br><span>                                      == object ifTrue: [key := i printString]]]].</span><br><span>                      key == nil ifTrue: [(parent isMorph and: [object isKindOf: Array]) ifTrue: [key := 'submorphs?']].</span><br><span>                      key == nil ifTrue: [key := '???'].</span><br><span>                      pointerList add: key , ': ' , object class name, (object isMorph ifTrue: [' (', object identityHash asString, ')'] ifFalse: [ String empty ]) ].</span><br><span>              objectList add: object.</span><br><span>              parent := object]!</span><br><span></span><br><span>Item was added:</span><br><span>+ ----- Method: PointerFinder>>excludedObjects (in category 'accessing') -----</span><br><span>+ excludedObjects</span><br><span>+ </span><br><span>+     ^ excludedObjects ifNil: [excludedObjects := OrderedCollection new]!</span><br><span></span><br><span>Item was added:</span><br><span>+ ----- Method: PointerFinder>>excludedObjects: (in category 'accessing') -----</span><br><span>+ excludedObjects: aCollection</span><br><span>+ </span><br><span>+     excludedObjects := aCollection!</span><br><span></span><br><span>Item was changed:</span><br><span>  ----- Method: PointerFinder>>followObject: (in category 'application') -----</span><br><span>  followObject: anObject</span><br><span>+ </span><br><span>+     (self excludedObjects includes: anObject)</span><br><span>+         ifTrue: [^ false].</span><br><span>      anObject outboundPointersDo: [:ea |</span><br><span>          (self follow: ea from: anObject)</span><br><span>              ifTrue: [^ true]].</span><br><span>      ^ false!</span><br><span></span><br><span>Item was changed:</span><br><span>  ----- Method: PointerFinder>>initialize (in category 'application') -----</span><br><span>  initialize</span><br><span>      parents := IdentityDictionary new: 20000.</span><br><span>      parents at: Smalltalk put: nil.</span><br><span>      parents at: Processor put: nil.</span><br><span>      parents at: self put: nil.</span><br><span></span><br><span>      toDo := OrderedCollection new: 5000.</span><br><span>      toDo add: Smalltalk.</span><br><span>+     toDoNext := OrderedCollection new: 5000.!</span><br><span>-     toDoNext := OrderedCollection new: 5000!</span><br><span></span><br><span></span><br></div></blockquote></div></body></html>