! PATCH FOR BUG 45358 ! ! Adds new private method _bug45358 to "touch" each LargeObjectNode ! in a large collection. The patch also replaces each method that ! calls an affected primitive with a new version that calls _bug45358 ! before executing the original code -- now represented as a method ! with the original name but prefixed with an underscore. ! ! Note that this patch by-passes the bug by doing a no-op write to the ! first element of each LargeObjectNode that makes up the collection, ! insuring that they have all been loaded into the VM's memory. ! This will result in a small performance impact and a greater load ! on the VM's memory, potentially causing out-of-memory errors. ! There will also be a greater volume of tranlog entries as the no-op ! write will force all the LargeObjectNodes to be written to the tranlogs ! during a commit. ! ! Affected methods/primitives ! ! SequenceableCollection >> insertAll:at: 606 ! SequenceableCollection >> insertObject:at: 398 ! SequenceableCollection >> removeFrom:to: 601 ! ! Array>>replaceFrom:to:with:startingAt: 608 ! Array>>_insertAt:from:fromStart:fromEnd:numToMoveDown: 605 ! Array>>_largeInsertAt:from:fromStart:fromEnd:numToMoveDown: 552 ! Array>>_deleteNoShrinkFrom:to: 602 ! Array>>_deleteNoShrinkFrom:to:anchorTailSize: 617 ! ! OrderedCollection>>replaceFrom:to:with:startingAt: 608 ! ! SortedCollection >> _insertAll:at: 606 ! SortedCollection >> _insertObject:at: 398 ! category: 'BUG45358' method: SequenceableCollection _bug45358 | size | size := self size. (size > 2034) ifTrue: [ 1 to: size by: 2034 do: [:i | (self _at: i put: (self _at: i))]]. ^ self % category: 'Adding' method: SequenceableCollection insertAll: aCollection at: anIndex self _bug45358. ^ self _insertAll: aCollection at: anIndex % category: 'Adding' method: SequenceableCollection _insertAll: aCollection at: anIndex "Inserts all the elements of aCollection into the receiver beginning at index anIndex. Returns aCollection. The argument anIndex must be greater than or equal to one. If anIndex is one greater than the size of the receiver, appends aCollection to the receiver. If anIndex is more than one greater than the size of the receiver, generates an error." | array argCls | argCls := aCollection class . (aCollection isKindOf: Interval) ifTrue:[ self insertAll: aCollection asArray at: anIndex . ^ aCollection ]. ((argCls isSubclassOf: CharacterCollection ) or:[ argCls isSubclassOf: ByteArray ]) ifTrue:[ array := Array new: aCollection size . 1 to: aCollection size do:[:j| array at: j put: (aCollection at: j) ]. self insertAll: array at: anIndex . ^ aCollection ]. aCollection _validateClasses: { Array . OrderedCollection . SequenceableCollection }. (anIndex _isInteger) ifFalse: [ ^ self _errorNonIntegerIndex: anIndex]. (anIndex < 1 or: [ anIndex > (self size + 1)]) "out of bounds" ifTrue: [ ^ self _errorIndexOutOfRange: anIndex]. ^ self _primitiveFailed: #insertAll:at: args: { aCollection . anIndex } % category: 'Adding' method: SequenceableCollection insertObject: anObject at: anIndex self _bug45358. ^ self _insertObject: anObject at: anIndex % category: 'Adding' method: SequenceableCollection _insertObject: anObject at: anIndex "Inserts anObject into the receiver at index anIndex and returns anObject." "primitive fails if receiver is byte format" self insertAll: { anObject } at: anIndex . ^ anObject % category: 'Removing' method: SequenceableCollection removeFrom: startIndex to: stopIndex self _bug45358. ^ self _removeFrom: startIndex to: stopIndex % category: 'Removing' method: SequenceableCollection _removeFrom: startIndex to: stopIndex "Removes the elements of the receiver from startIndex to stopIndex inclusive. Returns the receiver. The size of the receiver is decreased by stopIndex - startIndex + 1." (stopIndex < startIndex) ifTrue: [ ^ startIndex _error: #rtErrBadCopyFromTo args: { stopIndex }]. ((stopIndex > self size) or: [(stopIndex < 1)]) ifTrue: [ ^ self _errorIndexOutOfRange: stopIndex]. (startIndex < 1) ifTrue: [ ^ self _errorIndexOutOfRange: startIndex]. ((self isKindOf: CharacterCollection ) or:[ self isKindOf: ByteArray ]) ifTrue:[ | range | range := stopIndex - stopIndex + 1. startIndex to: stopIndex do: [ :i | self at: i put: (self at: (i + range))]. self size: (self size - range). ^ self ]. ^ self _primitiveFailed: #removeFrom:to: args: { startIndex . stopIndex } % category: 'Copying' method: OrderedCollection replaceFrom: startIndex to: stopIndex with: aSeqCollection startingAt: repIndex self _bug45358. ^ self _replaceFrom: startIndex to: stopIndex with: aSeqCollection startingAt: repIndex % category: 'Copying' method: OrderedCollection _replaceFrom: startIndex to: stopIndex with: aSeqCollection startingAt: repIndex "Replaces the elements of the receiver between the indexes startIndex and stopIndex inclusive with the elements of aSeqCollection starting at repIndex. If aSeqCollection is identical to the receiver, the source and destination blocks may overlap. Returns the receiver." startIndex _isSmallInteger ifFalse:[ startIndex _validateClass: SmallInteger ]. stopIndex _isSmallInteger ifFalse:[ stopIndex _validateClass: SmallInteger ]. repIndex _isSmallInteger ifFalse:[ repIndex _validateClass: SmallInteger ]. ^ super replaceFrom: startIndex to: stopIndex with: aSeqCollection startingAt: repIndex % category: 'Copying' method: Array replaceFrom: startIndex to: stopIndex with: aCollection startingAt: repIndex self _bug45358. ^ self _replaceFrom: startIndex to: stopIndex with: aCollection startingAt: repIndex % category: 'Copying' method: Array _replaceFrom: startIndex to: stopIndex with: aCollection startingAt: repIndex "Replaces the elements of the receiver between the indexes startIndex and stopIndex inclusive with the elements of aSeqCollection starting at repIndex. If aCollection is identical to the receiver, the source and destination blocks may overlap. aCollection must be a kind of SequenceableCollection or a kind of IdentityBag. Returns the receiver." startIndex _isSmallInteger ifFalse:[ startIndex _validateClass: SmallInteger ]. stopIndex _isSmallInteger ifFalse:[ stopIndex _validateClass: SmallInteger ]. repIndex _isSmallInteger ifFalse:[ repIndex _validateClass: SmallInteger ]. ^ super replaceFrom: startIndex to: stopIndex with: aCollection startingAt: repIndex % category: 'Copying' method: Array _insertAt: destOffset from: anArray fromStart: startOffset fromEnd: endOffset numToMoveDown: numToMove self _bug45358. ^ self __insertAt: destOffset from: anArray fromStart: startOffset fromEnd: endOffset numToMoveDown: numToMove % category: 'Copying' method: Array __insertAt: destOffset from: anArray fromStart: startOffset fromEnd: endOffset numToMoveDown: numToMove "Intended for use in manipulating objects which are nodes of B-trees or similar structures. Inserts the portion of anArray from startOffset to endOffset into the receiver beginning at destOffset. If anArray is nil, then (endOffset - startOffset + 1) nils are inserted into the receiver. The indexable instance variables of the receiver from destOffset to (destOffset + numToMove) are moved towards the end of the receiver by the amount (endOffset - startOffset + 1). The receiver must be a small object. If the receiver must be grown, it will be grown by 10 times the number of OOPs inserted up to a max of 500 oops. The receiver will not be grown larger than the max size of a small object. If the number of inserted oops alone will cause the object to be larger than the max size of a small object, then an error will be generated. Generates an error if any of the arguments imply the receiver must be grown to be a large object, or if destOffset is beyond the end of the receiver." | numInserted newSize | anArray ~~ nil ifTrue:[ anArray _validateClass: Array ]. (destOffset < 1 or:[ destOffset > self _basicSize] ) ifTrue:[ self _errorIndexOutOfRange: destOffset] . (startOffset < 1) ifTrue:[ self _errorIndexOutOfRange: startOffset] . (endOffset < 1) ifTrue:[ self _errorIndexOutOfRange: endOffset] . (numToMove < 0) ifTrue:[ self _errorIndexOutOfRange: numToMove] . (endOffset < startOffset) ifTrue:[ self _errorIndexOutOfRange: endOffset ]. (anArray ~~ nil and: [startOffset > anArray _basicSize]) ifTrue:[ anArray _errorIndexOutOfRange: startOffset]. (anArray ~~ nil and: [endOffset > anArray _basicSize]) ifTrue:[ anArray _errorIndexOutOfRange: endOffset]. numInserted := endOffset - startOffset + 1 . newSize := destOffset + numInserted + numToMove . newSize > 2034 "virtual machine constant OBJ_OOPS_SIZE" ifTrue:[ self _error: #objErrMaxSize args:{ 2034 "virtual machine constant" . newSize }. ]. self _primitiveFailed: #_insertAt:from:fromStart:fromEnd:numToMoveDown: args: { destOffset . anArray . startOffset . endOffset . numToMove } % category: 'Copying' method: Array _largeInsertAt: destOffset from: anArray fromStart: startOffset fromEnd: endOffset numToMoveDown: numToMove self _bug45358. ^ self __largeInsertAt: destOffset from: anArray fromStart: startOffset fromEnd: endOffset numToMoveDown: numToMove % category: 'Copying' method: Array __largeInsertAt: destOffset from: anArray fromStart: startOffset fromEnd: endOffset numToMoveDown: numToMove "Intended for use in manipulating objects which are nodes of B-trees or similar structures. Similar to primitive 605, except that with this primitive objects will be grown to large objects if necessary. Inserts the portion of anArray from startOffset to endOffset into the receiver beginning at destOffset. If anArray is nil, then (endOffset - startOffset + 1) nils are inserted into the receiver. The indexable instance variables of the receiver from destOffset to (destOffset + numToMove) are moved towards the end of the receiver by the amount (endOffset - startOffset + 1). The receiver may be a small or large object. If the receiver must be grown, it will be grown by 10 times the number of OOPs inserted up to a max of 500 oops. Generates an error if destOffset is beyond the end of the receiver." anArray ~~ nil ifTrue:[ anArray _validateClass: Array ]. (destOffset < 1 or:[ destOffset > self _basicSize] ) ifTrue:[ self _errorIndexOutOfRange: destOffset] . (startOffset < 1) ifTrue:[ self _errorIndexOutOfRange: startOffset] . (endOffset < 1) ifTrue:[ self _errorIndexOutOfRange: endOffset] . (numToMove < 0) ifTrue:[ self _errorIndexOutOfRange: numToMove] . (endOffset < startOffset) ifTrue:[ self _errorIndexOutOfRange: endOffset ]. (anArray ~~ nil and: [startOffset > anArray _basicSize]) ifTrue:[ anArray _errorIndexOutOfRange: startOffset]. (anArray ~~ nil and: [endOffset > anArray _basicSize]) ifTrue:[ anArray _errorIndexOutOfRange: endOffset]. self _primitiveFailed: #_largeInsertAt:from:fromStart:fromEnd:numToMoveDown: args: { destOffset . anArray . startOffset . endOffset . numToMove } % category: 'Removing' method: Array _deleteNoShrinkFrom: startIndex to: stopIndex self _bug45358. ^ self __deleteNoShrinkFrom: startIndex to: stopIndex % category: 'Removing' method: Array __deleteNoShrinkFrom: startIndex to: stopIndex "The elements of the receiver from startIndex to stopIndex are deleted from the receiver. And the tail of the receiver is filled with (stopIndex - startIndex + 1) number of nils. The size of the receiver is not changed. Both startIndex and stopIndex must be positive integers not larger than the size of the receiver, with startIndex <= stopIndex." (stopIndex < startIndex) ifTrue: [ ^ startIndex _error: #rtErrBadCopyFromTo args: { stopIndex }]. ((stopIndex > self size) or: [(stopIndex < 1)]) ifTrue: [ ^ self _errorIndexOutOfRange: stopIndex]. (startIndex < 1) ifTrue: [ ^ self _errorIndexOutOfRange: startIndex]. ^ self _primitiveFailed: #_deleteNoShrinkFrom:to: args: { startIndex . stopIndex } % category: 'Private' method: Array _deleteNoShrinkFrom: startIndex to: stopIndex anchorTailSize: tailSize self _bug45358. ^ self __deleteNoShrinkFrom: startIndex to: stopIndex anchorTailSize: tailSize % category: 'Private' method: Array __deleteNoShrinkFrom: startIndex to: stopIndex anchorTailSize: tailSize "Same as _deleteNoShrinkFrom: startIndex to: stopIndex except the last tailSize objects remain in the tail of the array. The size of the receiver does not change and the gap created by the delete operation is filled with nils. This method can fail for the following reasons: -receiver participates in an index. -receiver is a large object. -the delete operation requested would delete any part of the tail The size of the receiver is not changed. Both startIndex and stopIndex must be positive integers not larger than the size of the receiver, with startIndex <= stopIndex. " (stopIndex < startIndex) ifTrue: [ ^ startIndex _error: #rtErrBadCopyFromTo args: { stopIndex }]. ((stopIndex > self size) or: [(stopIndex < 1)]) ifTrue: [ ^ self _errorIndexOutOfRange: stopIndex]. (startIndex < 1) ifTrue: [ ^ self _errorIndexOutOfRange: startIndex]. (stopIndex > (self size - tailSize) ) ifTrue: [ ^ self _errorIndexOutOfRange: tailSize]. ^ self _primitiveFailed: #_deleteNoShrinkFrom:to:anchorTailSize: args: { startIndex . stopIndex . tailSize } % category: 'Private' method: SortedCollection _insertAll: aCollection at: anIndex self _bug45358. ^ self __insertAll: aCollection at: anIndex % category: 'Private' method: SortedCollection __insertAll: aCollection at: anIndex "Inserts all the elements of aCollection into the receiver beginning at index anIndex. Returns aCollection." "The argument anIndex must be greater than or equal to one. If anIndex is one greater than the size of the receiver, appends aCollection to the receiver. If anIndex is more than one greater than the size of the receiver, generates an error." ( self _hasModificationTracking and: [ aCollection isKindOf: CharacterCollection ] ) ifTrue: [ " primitive failed because aCollection is not an Array or OrderedCollection" ^ self insertAll: (Array withAll: aCollection) at: anIndex ]. self _primitiveFailed: #_insertAll:at: args: { aCollection . anIndex } % category: 'Adding' method: SortedCollection _insertObject: anObject at: anIndex self _bug45358. ^ self __insertObject: anObject at: anIndex % category: 'Adding' method: SortedCollection __insertObject: anObject at: anIndex "Inserts anObject into the receiver at index anIndex and returns anObject." "primitive fails if receiver is byte format" (anIndex _isInteger) ifFalse: [ ^ self _errorNonIntegerIndex: anIndex]. (anIndex < 1 or: [ anIndex > (self size + 1)]) "out of bounds" ifTrue: [ ^ self _errorIndexOutOfRange: anIndex]. ^ self _primitiveFailed: #_insertAll:at: args: { anObject . anIndex } %