From VisualWorks®, 7.6 of February 1, 2008 on February 1, 2008 at 12:29:36 pm (Dialog confirm: 'You are filing-in a Parcel source file!\\While this is possible it will not have\the same effect as loading the parcel.\None of the Parcel''s prerequisites will\be loaded and none of its load actions\will be performed.\\Are you sure you want to file-in?' withCRs) ifFalse: [self error: 'Parcel file-in abandoned. Choose terminate or close.'] <class> <name>UnhandledNoModificationException</name> <environment>Core</environment> <super>Core.UnhandledException</super> <private>false</private> none <imports></imports> <category>Kernel-Exception Handling</category> <attributes> <package>ImmutabilityCompatibility</package> </attributes> </class> <comment> Core.UnhandledNoModificationException
UnhandledNoModificationException exists only to raise a special notifier for an unhandled NoModificationError. The special notifier allows the user to specify how unhandled NoModificationErrors are treated. The different treatments allow backward-compatible supressing and retry of NoModificationErrors to allow old code that e.g. does modify literals to continue to run. This class is for backward-compatibility and will be removed from the base in a subsequent release.
</comment> <name>ErrorHandlingMode</name> <environment>Core.NoModificationError</environment> <private>false</private> <constant>false</constant> <category>private</category> <initializer>#error</initializer> <attributes> <package>ImmutabilityCompatibility</package> </attributes> <methods> Core.UnhandledNoModificationException <category>private - actions</category>
defaultAction "What to do when the exception is unhandled." | emergencyHandler | (emergencyHandler := self class emergencyHandler) == nil ifFalse: [^emergencyHandler value: parameter value: initialContext]. self aboutToDebug. ^Notifier openNoModificationErrorNotifierForContext: initialContext error: parameter label: self description proceedable: self isResumable
</methods> <methods> Core.NoModificationError class <category>backward compatibility accessing</category>
errorMode ^ErrorHandlingMode
errorMode: aSymbol (#(warn ignore error) includes: aSymbol) ifFalse: [^self error: 'invalid error mode']. ErrorHandlingMode := aSymbol
</methods> <methods> Core.NoModificationError <category>private - actions</category>
defaultAction "An attempt was made to modify an immutable object. What we do depends on the setting of ErrorHandlingMode. The silent approach makes the object mutable and performs the change, leaving the object mutable. Others will either continue the exception or place messages in the transcript." ErrorHandlingMode == #error ifTrue: [^super defaultAction]. "Print a stack trace. If we encounter a different immutability error in the printing, make sure we get a debugger rather than cycling forever" ErrorHandlingMode == #warn ifTrue: [[self printWarningMessageStackDepth: 3] on: NoModificationError do: [:ex | ^super defaultAction]]. (ErrorHandlingMode == #ignore or: [ErrorHandlingMode == #warn]) ifTrue: [self isResumable: true. self resume: self makeObjectsMutableAndRetry]. self error: 'Invalid ErrorHandlingMode for NoModificationError'.
makeObjectsMutableAndRetry self receiver isImmutable: false. "primBecome: is special because it is symmetric; it fails if ether the receiver or the argument are immutable. So check to ensure both are mutable." (selector == #primBecome: and: [value isImmutable]) ifTrue: [value isImmutable: false]. ^self retryModification
noHandler "Pass the receiver on to the UnhandledNoModificationException" ^UnhandledNoModificationException new parameter: self; searchFrom: initialContext; originator: originator; isResumable: self isResumable; raiseSignal.
printWarningMessageStackDepth: anInteger | context | Transcript cr; cr; nextPutAll: 'Object Mutation Exception (For more information, see Settings>Immutability)'. context := self initialContext. anInteger timesRepeat: [context isNil ifTrue: [^self]. Transcript crtab; print: context. context := context sender]. Transcript flush
propagate ^ErrorHandlingMode == #ignore ifTrue: [self isResumable: true. self resume: self makeObjectsMutableAndRetry] ifFalse: [super propagate].
</methods> <methods> UI.UISettings class <category>user preferences</category>
addImmutabilitySectionTo: aDictionary development: devSpecs runtime: runtimeSpecs "Add the UIPainter's set of preferences and settings slices to the arguments." "UI.UISettings addPreferenceSection: #addImmutabilitySectionTo:development:runtime:; updateAllPreferences'" aDictionary at: #NoModificationErrorErrorMode put:((PluggableAdaptor on: NoModificationError) getBlock: [:nme| | v | (v := self preferenceFor: #NoModificationErrorErrorModeEditing) isNil ifTrue: [nme errorMode] ifFalse: [v]] putBlock: [:nme :v| self preferenceFor: #NoModificationErrorErrorModeEditing put: v] updateBlock: [:nme :v :p| true]). aDictionary at: #immutabilityAccept put: [self immutabilityAccept]; at: #immutabilityReset put: [self immutabilityReset]. devSpecs add: 'Immutability'->#(#immutabilitySpec #immutabilityHelp)
</methods> <methods> UI.UISettings class <category>accessing preferences</category>
immutabilityAccept NoModificationError errorMode: (self preferenceFor: #NoModificationErrorErrorModeEditing). self immutabilityReset
immutabilityReset self preferenceFor: #NoModificationErrorErrorMode put: nil
</methods> <methods> UI.UISettings <category>help</category>
immutabilityHelp ^'Immutability Backward Compatibility Support VisualWorks 7 adds Immutability support. Immutable objects will raise an exception if you attempt to modify them. By default, objects like method literals are now immutable. This can break old code. For example '''' writeStream nextPutAll: ''test'' will no longer work. This was likely an error in the old code too, but now it is detected. To enable a more graceful transition to the immutability code, we allow a couple of variations in the handling of immutability errors. Note that you are still better off fixing the immutability problems, as the code will run significantly faster. For more information, see the VisualWorks documentation. This dialog allows you to choose from three options when a NoModificationError is encountered. Note that if you choose an option which includes making the object mutable and later decide to fix the error at its source, you will probably not be able to reproduce the problem until the code in question has been recompiled. This is because the literal is stored in the compiled method and will remain mutable until the code is recompiled or loaded into a new image. Signal an error -- This is the default. An exception is raised, and if it is not handled elsewhere then a Notifier will be presented. This is a special Notifier which allows you to choose a different immutability error handling option and proceed. The options you can choose in the Notifier are exactly the same options you can choose here, but of course the notifier is only shown if this option is in effect.You can still make changes in the settings no matter what you did in the Notifier. Print a warning and make the object mutable -- An exception is raised, but if the exception is not handled then the default action is to print a short message to the Transcript listing the method in which the error occurred (a 3 line stack trace). The receiving object is made mutable and the operation raising the exception is restarted. No notification is made that this occurred, and the receiving object remains mutable. Note that this is not equivalent to #whileMutableDo:, which restores the immutability of the object once the action completes. Silently make the object mutable -- No exception is raised. The receiving object is made mutable and the operation proceeds. No notification is made that this occurred, and the receiving object remains mutable, as above.'
</methods> <methods> Tools.Notifier class <category>instance creation</category>
openNoModificationErrorNotifierForContext: haltContext error: noModificationError label: aString proceedable: aBoolean | headlessImage isHeadless doesntKnowAboutTheMessage | headlessImage := #{HeadlessImage} ifDefinedDo: [:headlessImageClass | headlessImageClass default] elseDo: [nil]. "If the special noModificationNotifier isn't present (e.g. has been stripped) or if we are in a headless image, then use a normal notifier (which already handles the headless case)" isHeadless := headlessImage notNil and: [headlessImage isHeadless]. doesntKnowAboutTheMessage := (current respondsTo: #openNoModificationErrorNotifierForContext:error:label:proceedable:) not. isHeadless | doesntKnowAboutTheMessage ifTrue: [self openContext: haltContext label: aString proceedable: aBoolean] ifFalse: [current openNoModificationErrorNotifierForContext: haltContext error: noModificationError label: aString proceedable: aBoolean].
</methods> <methods> Tools.NotifierView class <category>instance creation</category>
openNoModificationErrorNotifierForContext: haltContext error: noModificationError label: aString proceedable: aBoolean "Create and schedule an instance of me viewing a Debugger on haltContext. The view will be labeled with aString, and will show a short sender stack. Include information that informs the user as to the NoModificationError default werror treatments available and allows one to change them." | displayPoint contentsString debugger | self prepareForDebugging. contentsString := self shortStackFor: haltContext ofSize: 5. displayPoint := self getDisplayPoint. debugger := self debuggerClass context: haltContext proceedable: aBoolean. self openNoModificationNotifier: debugger error: noModificationError contents: contentsString label: aString displayAt: displayPoint. thisContext unwindUpTo: haltContext. Processor activeProcess suspend
</methods> <methods> Tools.NotifierView class <category>private</category>
openNoModificationNotifier: aDebugger error: theNoModificationError contents: aString1 label: aString2 displayAt: aPoint | box text helpText y builder label actions specs width height copyStack baseFraction radio errorModeRadioModel left radioButtonTop groupBox | self logErrorFor: aDebugger label: aString2. errorModeRadioModel := NoModificationError errorMode asValue. aDebugger prepareForErrorCondition. builder := UIBuilder new. builder windowOn: aDebugger label: 'Exception'. aDebugger label: aString2. text := aString2 asText. text emphasizeFrom: 1 to: text size with: (Screen default colorDepth < 4 ifTrue: [#bold] ifFalse: [Array with: #bold with: #color->(ColorValue red: 0.8 green: 0 blue: 0)]). text := ComposedText withText: text style: nil compositionWidth: 250. y := 10. label := LabelSpec new hasCharacterOrientedLabel: false. label setLabel: builder policy alertIcon. label layout: (AlignmentOrigin new leftOffset: 48; topOffset: y + (text bounds height - builder policy alertIcon bounds height // 2 max: 0); leftAlignmentFraction: 1). builder add: label. label := LabelSpec new hasCharacterOrientedLabel: false. label setLabel: text. label layout: (AlignmentOrigin new leftOffset: 56; topOffset: y; leftAlignmentFraction: 0). y := y + text bounds height + 8. builder add: label. helpText := ComposedText withText: 'It is an error in VW7 to modify immutable objects such as literals. For backward compatibility, you can disable these errors by selecting a different immutability policy below and proceeding. This policy can also be changed using Settings>Immutability.' asText style: nil. label := LabelSpec new hasCharacterOrientedLabel: false. label setLabel: helpText. label layout: (AlignmentOrigin new leftOffset: 20; topOffset: y; leftAlignmentFraction: 0). y := y + helpText bounds height + 8. builder add: label. radioButtonTop := y. y := y + 24. left := 40. text := ComposedText withText: 'Signal an error' asText style: nil. radio := RadioButtonSpec model: errorModeRadioModel label: text select: #error. radio layout: (AlignmentOrigin new leftOffset: left; topOffset: y). builder add: radio. y := y + text bounds height. text := ComposedText withText: 'Print a warning and make the object mutable' asText style: nil. radio := RadioButtonSpec model: errorModeRadioModel label: text select: #warn . radio layout: (AlignmentOrigin new leftOffset: left; topOffset: y). builder add: radio. y := y + text bounds height. text := ComposedText withText: 'Silently make the object mutable' asText style: nil. radio := RadioButtonSpec model: errorModeRadioModel label: text select: #ignore . radio layout: (AlignmentOrigin new leftOffset: left; topOffset: y). builder add: radio. y := y + builder wrapper preferredBounds height + 16. text := ComposedText withText: 'On an attempt to modify an immutable object' asText style: nil. groupBox := GroupBoxSpec label: text. groupBox layout: (LayoutFrame leftFraction: 0 offset: 20 rightFraction: 1 offset: -20 topFraction: 0 offset: radioButtonTop bottomFraction: 0 offset: y). builder add: groupBox. y := y + 16. actions := Array with: 'Debug' -> [NoModificationError errorMode: errorModeRadioModel value. aDebugger class openFullViewOn: aDebugger label: aString2. builder window model: nil. builder window controller close] with: 'Proceed' -> ["Sigh; exceptions don't really proceed correctly, instead we resume execution in the context that raised the error. Better would be to proceed from the noHandler method. Because this doesn't happen we have to retry here explicitly." #error ~~ errorModeRadioModel value ifTrue: [NoModificationError errorMode: errorModeRadioModel value. theNoModificationError makeObjectsMutableAndRetry]. aDebugger proceed. builder window controller close] with: 'Terminate' -> [NoModificationError errorMode: errorModeRadioModel value. builder window controller close]. specs := OrderedCollection new. 1 to: actions size do: [:index | | action | action := actions at: index. specs add: (ActionButtonSpec model: action value label: action key layout: (LayoutFrame new leftFraction: index - 1 / actions size; leftOffset: (index = 1 ifTrue: [20] ifFalse: [0]); rightFraction: index / actions size; rightOffset: (index = actions size ifTrue: [-20] ifFalse: [0]); topOffset: y; bottomOffset: y)). specs last defaultable: true. action key = 'Debug' ifTrue: [specs last isDefault: true. builder add: specs last. builder keyboardProcessor setActive: builder wrapper widget controller] ifFalse: [builder add: specs last]. (action key = 'Proceed' and: [aDebugger mayProceed not]) ifTrue: [builder wrapper disable]]. y := y + builder wrapper preferredBounds height. specs do: [:spec | spec layout bottomOffset: y]. baseFraction := actions size - 1 / 2 / actions size. copyStack := (ActionButtonSpec model: [aDebugger copyStack] label: 'Copy stack' layout: (LayoutFrame new leftFraction: baseFraction; rightFraction: baseFraction + (1 / actions size); topOffset: y; bottomOffset: y + builder wrapper preferredBounds height)). copyStack defaultable: true. builder add: copyStack. y := y + builder wrapper preferredBounds height + 8. label := LabelSpec new hasCharacterOrientedLabel: false. label setLabel: aString1 asComposedText. label layout: (AlignmentOrigin new leftFraction: 0.5 offset: 0; topOffset: y; leftAlignmentFraction: 0.5). builder add: label. y := y + builder wrapper preferredBounds height . width := (420 max: label getLabel bounds width + 12) max: helpText bounds width + 20. height := y + 10. box := 0 @ 0 corner: width @ height. box := box align: box center with: aPoint. [builder openIn: box] on: GetWindowManagerUsagePolicy do: [:ex | ex resume: MakeNewWindowManagerUsagePolicy new]
</methods> <methods> UI.UISettings class <category>interface specs</category>
immutabilitySpec "UIPainter new openOnClass: self andSelector: #immutabilitySpec" <resource: #canvas> ^#(#{UI.FullSpec} #window: #(#{UI.WindowSpec} #label: 'Unlabeled Canvas' #min: #(#{Core.Point} 50 50 ) #bounds: #(#{Graphics.Rectangle} 547 229 812 501 ) ) #component: #(#{UI.SpecCollection} #collection: #( #(#{UI.GroupBoxSpec} #layout: #(#{Graphics.LayoutFrame} 8 0 8 0 -8 1 112 0 ) #name: #GroupBox1 #label: 'On an attempt to modify an immutable object' ) #(#{UI.RadioButtonSpec} #layout: #(#{Core.Point} 24 32 ) #name: 'RadioButton1' #model: #NoModificationErrorErrorMode #callbacksSpec: #(#{UI.UIEventCallbackSubSpec} #valueChangeSelector: #disturb ) #tabable: true #label: 'Signal an error' #select: #error ) #(#{UI.RadioButtonSpec} #layout: #(#{Core.Point} 24 56 ) #name: #RadioButton2 #model: #NoModificationErrorErrorMode #callbacksSpec: #(#{UI.UIEventCallbackSubSpec} #valueChangeSelector: #disturb ) #tabable: true #label: 'Print a warning and make the object mutable' #select: #warn ) #(#{UI.RadioButtonSpec} #layout: #(#{Core.Point} 24 80 ) #name: #RadioButton3 #model: #NoModificationErrorErrorMode #callbacksSpec: #(#{UI.UIEventCallbackSubSpec} #valueChangeSelector: #disturb ) #tabable: true #label: 'Silently make the object mutable' #select: #ignore ) #(#{UI.ActionButtonSpec} #layout: #(#{Core.Point} 10 240 ) #name: #acceptButton #flags: 40 #model: #immutabilityAccept #callbacksSpec: #(#{UI.UIEventCallbackSubSpec} #valueChangeSelector: #undisturb ) #label: 'Accept' #isDefault: true #defaultable: true ) #(#{UI.ActionButtonSpec} #layout: #(#{Core.Point} 101 240 ) #name: #resetButton #flags: 40 #model: #immutabilityReset #callbacksSpec: #(#{UI.UIEventCallbackSubSpec} #valueChangeSelector: #undisturb ) #label: 'Reset' #defaultable: true ) #(#{UI.ActionButtonSpec} #layout: #(#{Core.Point} 185 240 ) #name: #helpButton #model: #helpAction #label: 'Help' #defaultable: true ) ) ) )
</methods> <methods> Tools.VisualWorksSettings class <category>pages</category>
immutabilityPage <settingsPage: #(system immutability)> ^ModularSettingsPage new label: #Immutability << #labels >> 'Immutability'; icon: (ListIconLibrary visualFor: #balloon); useRadioButtonsForEnumerations; addSetting: (self settingWithId: #(system immutabilityPolicy))
</methods> <methods> Tools.VisualWorksSettings class <category>settings-immutability</category>
immutabilityPolicy <setting: #(system immutabilityPolicy)> | typeDescription | typeDescription := EnumerationSetting choices: #(error warn ignore) labels: (Array with: #ImmutabilityError << #labels >> 'Signal an error' with: #ImmutabilityWarn << #labels >> 'Print a warning and make the object mutable' with: #ImmutabilityIgnore << #labels >> 'Silently make the object mutable'). ^(typeDescription on: NoModificationError aspect: #errorMode) label: #OnAnAttemptToModifyAnImmutable << #labels >> 'On an attempt to modify an immutable object'; helpText: #OnAnAttemptToModifyAnImmutableHelpText << #dialogs >> 'Immutability Backward Compatibility Support VisualWorks 7 adds Immutability support. Immutable objects will raise an exception if you attempt to modify them. By default, objects like method literals are now immutable. This can break old code. For example '''' writeStream nextPutAll: ''test'' will no longer work. This was likely an error in the old code too, but now it is detected. To enable a more graceful transition to the immutability code, we allow a couple of variations in the handling of immutability errors. Note that you are still better off fixing the immutability problems, as the code will run significantly faster. For more information, see the VisualWorks documentation. This dialog allows you to choose from three options when a NoModificationError is encountered. Note that if you choose an option which includes making the object mutable and later decide to fix the error at its source, you will probably not be able to reproduce the problem until the code in question has been recompiled. This is because the literal is stored in the compiled method and will remain mutable until the code is recompiled or loaded into a new image. Signal an error -- This is the default. An exception is raised, and if it is not handled elsewhere then a Notifier will be presented. This is a special Notifier which allows you to choose a different immutability error handling option and proceed. The options you can choose in the Notifier are exactly the same options you can choose here, but of course the notifier is only shown if this option is in effect.You can still make changes in the settings no matter what you did in the Notifier. Print a warning and make the object mutable -- An exception is raised, but if the exception is not handled then the default action is to print a short message to the Transcript listing the method in which the error occurred (a 3 line stack trace). The receiving object is made mutable and the operation raising the exception is restarted. No notification is made that this occurred, and the receiving object remains mutable. Note that this is not equivalent to #whileMutableDo:, which restores the immutability of the object once the action completes. Silently make the object mutable -- No exception is raised. The receiving object is made mutable and the operation proceeds. No notification is made that this occurred, and the receiving object remains mutable, as above.'
</methods> "Imported Classes:" self error: 'Attempting to file-in parcel imports. Choose terminate or close' <class> <name>UnhandledException</name> <environment>Core</environment> <super>Core.GenericException</super> <private>false</private> none <imports></imports> <category>Kernel-Exception Handling</category> <attributes> <package>Kernel-Exception Handling</package> </attributes> </class> <class> <name>UISettings</name> <environment>UI</environment> <super>UI.ApplicationModel</super> <private>false</private> none list disturbed subBuilder <imports></imports> <category>Interface-Support</category> <attributes> <package>Interface-Support</package> </attributes> </class> <class> <name>NoModificationError</name> <environment>Core</environment> <super>Core.Error</super> <private>false</private> none selector index value <imports></imports> <category>Kernel-Objects</category> <attributes> <package>Kernel-Objects</package> </attributes> </class>