[squeak-dev] [proposal] migrating preferences to new images: UndefinedClass

Lauren Pullen drurowin at gmail.com
Mon Apr 18 21:49:23 UTC 2022

Hi Marcel,

On 4/4/22 09:20, Marcel Taeumel wrote:
> If you could share more details on this issue, we might be able to re-configure the loading code for those (missing) classes. Otherwise, a generic "[ ... ] on: Error do: [:ex | ...]" might also help for a more robust "stream next" in Preferences class >> #loadPreferencesFrom:.
Take Metacello-GitHub.  It defines a pragma preference on
MCGitHubRepository.  If you try loading a my.prefs in an image where
Metacello-GitHub isn't loaded, preference loading will abruptly end.

> For more info on data streams, see implementors of:
> #classVersion
> #readDataFrom:size:
> #objectForDataStream:
> #withClassVersion:
I looked through ReferenceStream and followed along in the debugger and
some inspectors to get a feel for how it all worked.

Back to the MCGitHubRepository example, the ReferenceStream will try to
instantiate a MCGitHubRepository to fill the value for the 'provider'
instance variable of PragmaPreference... which rapidly fails unless it's

I've got a quick version saved in the attached file, but it's only a
partial fix and is likely full of issues and not stable enough to
introduce to the inbox, to see if I'm on the right track.

(	I changed the error class in the failing portion from Error to
KeyNotFound to match the class that SmalltalkImage>>at: signals to make
it easier to detect.

If this looks like a good direction to you, I'll proceed work on it.
-------------- next part --------------
'From Squeak5.3 of 30 November 2021 [latest update: #19461] on 15 April 2022 at 2:39:21 pm'!

!DiskProxy methodsFor: 'i/o' stamp: 'lrnp 4/15/2022 13:22'!
comeFullyUpOnReload: smartRefStream
	"Internalize myself into a fully alive object after raw loading from a DataStream. (See my class comment.)  DataStream will substitute the object from this eval for the DiskProxy."
	| globalObj symbol pr nn arrayIndex env |

	symbol := globalObjectName.
	"See if class is mapped to another name"
	(smartRefStream respondsTo: #renamed) ifTrue:
		[| maybeReadDataFromContext maybeReadArrayContext |
		"Ugh; so ugly and brittle.  If there were pragmas in the relevant methods we could search, etc. eem 7/3/2017 15:54"
		maybeReadArrayContext := thisContext sender sender sender sender.
		maybeReadDataFromContext := maybeReadArrayContext sender sender sender sender.
		"If in outPointers in an ImageSegment, remember original class name.  
		 See mapClass:installIn:.  Would be lost otherwise." "Anyone know where mapClass:installIn: is/was? eem 7/3/2017 15:55"
		(maybeReadDataFromContext method selector == #readDataFrom:size:
		 and: [maybeReadDataFromContext receiver class == NativeImageSegment
		 and: [maybeReadArrayContext method == (DataStream compiledMethodAt: #readArray)]]) ifTrue:
			[arrayIndex := maybeReadArrayContext tempAt: 4.
			"index var in readArray.  Later safer to find i on stack of context."
			smartRefStream renamedConv at: arrayIndex put: symbol].	"save original name"
		symbol := smartRefStream renamed at: symbol ifAbsent: [symbol]].	"map"
	env := Environment current.
	globalObj := env valueOf: symbol ifAbsent: [
		preSelector == nil & (constructorSelector = #yourself) ifTrue: [
			Transcript cr; show: symbol, ' is undeclared.'.
			env undeclare: symbol.
			^ nil].
		^ (KeyNotFound key: symbol) messageText: 'Global "', symbol, '" not found'; signal].
	((symbol == #World) and: [Smalltalk isMorphic not]) ifTrue: [
		self inform: 'These objects will work better if opened in a Morphic World.
Dismiss and reopen all menus.'].

	preSelector ifNotNil: [
		Symbol hasInterned: preSelector ifTrue: [:selector |
			[globalObj := globalObj perform: selector] on: Error do: [:ex |
				ex messageText = 'key not found' ifTrue: [^ nil].
				^ ex signal]]
	symbol == #Project ifTrue: [
		(constructorSelector = #fromUrl:) ifTrue: [
			nn := (constructorArgs first findTokens: '/') last.
			nn := (nn findTokens: '.|') first.
			pr := Project named: nn. 
			^ pr ifNil: [self] ifNotNil: [pr]].
		pr := globalObj perform: constructorSelector withArguments: constructorArgs.
		^ pr ifNil: [self] ifNotNil: [pr]].	"keep the Proxy if Project does not exist"

	constructorSelector ifNil: [^ globalObj].
	Symbol hasInterned: constructorSelector ifTrue: [:selector |
		[^ globalObj perform: selector withArguments: constructorArgs] on: Error do: [:ex |
			ex messageText = 'key not found' ifTrue: [^ nil].
			^ ex signal]
				"args not checked against Renamed"
	^ nil 	"was not in proper form"! !

!Preferences class methodsFor: 'initialization - save/load' stamp: 'lrnp 4/15/2022 14:21'!
	(FileDirectory default fileExists: 'my.prefs')
		ifTrue: [ Cursor wait showWhile: [
			[[ self loadPreferencesFrom: 'my.prefs' ]
					on: KeyNotFound
					do: [:ex | Transcript showln: 'Skipped preference for unloaded class ', ex key; flush. ex return: nil]]
				on: Error
				do: [ :ex | self inform: 'there was an error restoring the preferences' ]
		] ]
		ifFalse: [ self inform: 'you haven''t saved your preferences yet!!' ].
	! !

More information about the Squeak-dev mailing list