Saving morphs to file

Chris Muller chris at funkyobjects.org
Tue Jul 18 04:11:25 UTC 2006


Hmm, well your good question sent me on a big chase that led me to
enlightenment, thank you.  The short answer is "no".  The long answer
follows.

To my surprise and disappointment, it appears support for Blocks is not
as robust as I had thought.  It appears that blocks referencing inst
vars or "self" cannot be valued after rematerializing.  Blocks that
refer merely to their own arguments or globals or class-variables _can_
be valued after rematerializing.

This surprised me because the serialization test cases serialize four
different block fixtures:

blockSamples
	^ { 
	"self reference"
	[ self class name = #MaFixtureFactory ifFalse: [ self error: 'Block
home not correct' ] ].
	"global reference"
	[ : each | Transcript cr; show: each printString ].
	"local access only"
	[ : a : b | a name > b name ].
	"global and inst var reference"
	[ Transcript cr; show: allNonCollectionSamples size ] }

and then, upon rematerialization, performs the following comparison:

  maEquivalentForSerializationTest: aBlockContext 
	aBlockContext class = self class ifFalse: [ ^ false ].
	^ self method = aBlockContext method
	and: [ self numArgs = aBlockContext numArgs
	and: [ self startpc = aBlockContext startpc ] ]

and while all four blocks pass this test, this is apparently not
sufficient for valuing the ones that reference self or the inst-var.

I discovered this by trying it manually, created class TestObject with
inst var 'var2'.  I implemented:

  block2
	^ [ Transcript cr; show: var2 ]

and then tested it:

  MaObjectSerializer testMaterialize: (TestObject new var2: 'hello
world'; block2)

This serializes the block, then instantiates a brand new one from
scratch from the serialized bytes and returns it.

It looks real close in the fully-expanded explorer, but "self value"
printed 26258680 in the Transcript instead of "hello world".  What
the..?  Debugging into it shows: 

  DoIt
	^ [Transcript cr; show: unknown0]

So the inst var did not get "hooked up" or something.

I've never had a need to store complex blocks, since they exist in the
context of a method, and methods are typically stored in the
code-maangement system (i.e., change sets or Monticello) then it seems
storing Blocks -> MethodContexts -> CompiledMethods could lead to
serious confusion anyway.  e.g., you could make a change to the block
in the code and but persisted block would still be performing the old
code..  It would seem better to use an alternative like MessageSends
that logically calls the most up-to-date methods.

Still, this was a surprise because I did put some work into saving and
restoring BlockContexts, MethodContexts and CompiledMethods.  Each is
serialized into a "MaBlockContextStorage", "MaMethodContextStorage" and
"MaCompiledMethodStorage", respectively.

BlockContextStorage captures nargs, startpc, and home (a
MethodContextStorage).  MethodContextStorage captures the 'method' (a
CompiledMethodStorage).  CompiledMethodStorage captures 'header
literals startPc endPc byteCodes sourcePointer className'.  Restoration
works backward, first the CompiledMethod:

  asCompiledMethod
	| answer |
	answer _ 
		CompiledMethod 
			newMethod: endPc - startPc + 1 + 4
			header: header.
	literals withIndexDo: 
		[ : each : index |
		answer
			literalAt: index
			put: each ].
"	answer setSourcePointer: sourcePointer."  "<-- don't restore it, it
could be wrong."
"In fact..."
sourcePointer > 0 ifTrue: [ answer zapSourcePointer ].
	answer
		replaceFrom: startPc
		to: endPc
		with: byteCodes
		startingAt: 1.
	self restoreLiterals: answer.
	^ answer

then the MethodContext:

  asMethodContext
	| cm |
	^ (MethodContext newForMethod: (cm _ method asCompiledMethod))
		privRefreshWith: cm ;
		yourself

and, finally, the BlockContext:

  asBlockContext
	| methodContext |
	methodContext _ home asMethodContext.
	^ (BlockContext newForMethod: methodContext method)
		home: methodContext
		startpc: startpc
		nargs: nargs

Whew!  Sorry for this deluge, but if anyone has any suggestions for
improving this I would be grateful.

 - Chris



--- Andreas Raab <andreas.raab at gmx.de> wrote:

> Chris Muller wrote:
> > Hi Noury, if you are in 3.7 or 3.8 you can use Magma to transfer a
> > project containing Connected Morphs to another image.  Blocks and
> > CompiledMethods are no problem.
> 
> Out of curiosity, how does Magma deal with a situation where you
> serialize a block referring to an iVar and the shape of that class
> changes? Can you detect it?
> 
> Cheers,
>    - Andreas
> 
> 
> 




More information about the Squeak-dev mailing list