Hi. I am not sure if I understand which is the bug and it is difficult to do a clear diff. You mean that weak references should NOT be serialized ? While thinking about this in Fuel<a href="http://forum.world.st/Should-a-serializer-do-something-in-particular-with-weak-references-td3827593.html"> I asked in the mailing list</a> and the conclusion was that we shouldn't do anything special in Fuel for weak references. In this case, if I understand, you are saying the opposite: do something different for weak references, basically to consider them "transient". Can you please explain me why it is needed a specific behavior for weak references? <br>
<br>Thanks in advance!<br><br><br><div class="gmail_quote">On Sun, Nov 27, 2011 at 6:28 PM, Juan Vuletich <span dir="ltr"><<a href="mailto:juan@jvuletich.org">juan@jvuletich.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
Hi Folks,<br>
<br>
I found a bug in Cuis that made weak references to be improperly dumped in SmartRefStreams due to a bug in ReferenceStream. When I tried to reproduce the bug and fix in Squeak, I found out that in Squeak weak references are always dumped (and not just the more subtle bug in Cuis). The attach includes a test and fix. I'm confident about the fix in the context of Cuis, but I only played a few minutes with this in Squeak.<br>
<br>
Can anybody check this for Squeak and consider it for inclusion?<br>
<br>
Thanks,<span class="HOEnZb"><font color="#888888"><br>
Juan Vuletich<br>
</font></span><br>'From Squeak4.2 of 20 November 2011 [latest update: #11796] on 27 November 2011 at 2:56:53 pm'!<br>
TestCase subclass: #ReferenceStreamTest<br>
instanceVariableNames: ''<br>
classVariableNames: ''<br>
poolDictionaries: ''<br>
category: 'System-Object Storage-Tests'!<br>
<br>
!Object methodsFor: 'objects from disk' stamp: 'jmv 11/27/2011 14:56'!<br>
storeDataOn: aDataStream<br>
"Store myself on a DataStream. Answer self. This is a low-level DataStream/ReferenceStream method. See also objectToStoreOnDataStream. NOTE: This method must send 'aDataStream beginInstance:size:' and then (nextPut:/nextPutWeak:) its subobjects. readDataFrom:size: reads back what we write here."<br>
| cntInstVars cntIndexedVars |<br>
<br>
cntInstVars := self class instSize.<br>
cntIndexedVars := self basicSize.<br>
aDataStream<br>
beginInstance: self class<br>
size: cntInstVars + cntIndexedVars.<br>
1 to: cntInstVars do:<br>
[:i | aDataStream nextPut: (self instVarAt: i)].<br>
<br>
"Write fields of a variable length object. When writing to a dummy<br>
stream, don't bother to write the bytes"<br>
((aDataStream byteStream class == DummyStream) and: [self class isBits]) ifFalse: [<br>
self class isWeak<br>
ifTrue: [<br>
"For weak classes (for example DependentsArray) write the referenced object only<br>
if referenced from elsewhere in the dumped object graph.<br>
This means, for instance that if we only dump a model, no dependents are stored,<br>
but if we store a view (i.e. a Morph), it is properly handled as a dependent after the object graph is revived."<br>
1 to: cntIndexedVars do: [ :i |<br>
aDataStream nextPutWeak: (self basicAt: i)]]<br>
ifFalse: [<br>
1 to: cntIndexedVars do: [ :i |<br>
aDataStream nextPut: (self basicAt: i)]]]! !<br>
<br>
<br>
!DiskProxy methodsFor: 'i/o' stamp: 'jmv 11/25/2011 13:20'!<br>
storeDataOn: aReferenceStream<br>
"Besides just storing, get me inserted into references, so structures will know about class DiskProxy."<br>
<br>
super storeDataOn: aReferenceStream.<br>
<br>
"just so instVarInfo: will find it and put it into structures"<br>
" aReferenceStream references at: self put: #none."<br>
aReferenceStream addSpecialReference: self! !<br>
<br>
<br>
!ReferenceStream methodsFor: 'statistics' stamp: 'jmv 11/25/2011 13:21'!<br>
statisticsOfRefs<br>
"Analyze the information in references, the objects being written out"<br>
<br>
| parents ownerBags tallies n nm owners normalReferences |<br>
normalReferences := self references. "Exclude unrealized weaks"<br>
parents := IdentityDictionary new: normalReferences size * 2.<br>
n := 0.<br>
'Finding Owners...'<br>
displayProgressFrom: 0 to: normalReferences size<br>
during: [:bar |<br>
normalReferences keysDo:<br>
[:parent | | kids |<br>
bar value: (n := n+1).<br>
kids := parent class isFixed<br>
ifTrue: [(1 to: parent class instSize) collect: [:i | parent instVarAt: i]]<br>
ifFalse: [parent class isBits ifTrue: [Array new]<br>
ifFalse: [(1 to: parent basicSize) collect: [:i | parent basicAt: i]]].<br>
(kids select: [:x | normalReferences includesKey: x])<br>
do: [:child | parents at: child put: parent]]].<br>
ownerBags := Dictionary new.<br>
tallies := Bag new.<br>
n := 0.<br>
'Tallying Owners...'<br>
displayProgressFrom: 0 to: normalReferences size<br>
during: [:bar |<br>
normalReferences keysDo: "For each class of obj, tally a bag of owner classes"<br>
[:obj | | objParent | bar value: (n := n+1).<br>
nm := obj class name.<br>
tallies add: nm.<br>
owners := ownerBags at: nm ifAbsent: [ownerBags at: nm put: Bag new].<br>
(objParent := parents at: obj ifAbsent: [nil]) == nil<br>
ifFalse: [owners add: objParent class name]]].<br>
^ String streamContents:<br>
[:strm | tallies sortedCounts do:<br>
[:assn | n := assn key. nm := assn value.<br>
owners := ownerBags at: nm.<br>
strm cr; nextPutAll: nm; space; print: n.<br>
owners size > 0 ifTrue:<br>
[strm cr; tab; print: owners sortedCounts]]]! !<br>
<br>
!ReferenceStream methodsFor: 'writing' stamp: 'jmv 11/25/2011 13:20'!<br>
addSpecialReference: aDiskProxy<br>
"See senders. Added to avoid breaking encapsulation (assuming that #references would answer the actual collection)"<br>
references at: aDiskProxy put: #none! !<br>
<br>
!ReferenceStream methodsFor: 'writing' stamp: 'jmv 11/27/2011 14:56'!<br>
references<br>
"Do not include provisory references created in #nextPutWeak that never became normal references,<br>
because the referenced object was never added from a call to #nextPut:"<br>
^ references select: [ :value | value isNumber ]! !<br>
<br>
<br>
!ReferenceStreamTest methodsFor: 'testing' stamp: 'jmv 11/27/2011 14:56'!<br>
testWeakDumps<br>
"Test that if we serialize a model with weak references to views, only the model is serialized and not the views.<br>
<br>
Note: The bug became apparent only when dumping a model to a SmartRefStream, that calls #references, and the serialized stream<br>
was later materialized in an image where the view classes had been deleted. In such rare cases, materialization would fail when trying to reference these<br>
absent classes. If serializing to a ReferenceStream, the bug didn't become apparent (views were never serialized). If serializing to a SmartRefStream, but<br>
view classes still existed, the bug didn't really become apparent (because views were not actually deserialized), the only effect was a larger file.<br>
<br>
ReferenceStreamTest new testWeakDumps<br>
"<br>
| oldInstance refStream |<br>
oldInstance :=StringHolder new contents: 'This is a text'.<br>
oldInstance addDependent: Morph new.<br>
refStream := ReferenceStream on: (DummyStream on: nil).<br>
refStream nextPut: oldInstance.<br>
self deny: (refStream references keys anySatisfy: [ :dumpedObject | dumpedObject isKindOf: Morph ])! !<br>
<br>
<br><br>
<br></blockquote></div><br><br clear="all"><br>-- <br>Mariano<br><a href="http://marianopeck.wordpress.com" target="_blank">http://marianopeck.wordpress.com</a><br><br>