[Pharo-project] Fuel bug: infinite recursion in "analyzer when:substituteBy:"
The attached test case, reproduced below: | result | self analyzer when: [:x | FLPair = x class and: [x left isNil not] ] substituteBy: [:x | x copy left: $A ]. result := self resultOfSerializeAndMaterialize: (FLPair new left: $a; right: $b; yourself). self assert: result left = $A. causes an infinite loop with IdentityDictionary (see stack trace at end). The similar test case #testTransientPairLeft, works because the guard condition is negated by the replacement (i.e. "x left isNil not" is made false by the replacement). The attached test case does not make "x left isNil not" false. It causes an infinite recursion, so use "ctrl-." to stop it before too long. Is this a bug, or the intention of the #when:substituteBy: feature? I've worked around it, by making sure to always have the substituted value fail the guard clause. -- Yanni ==== stack trace ==== IdentityDictionary>>scanFor: IdentityDictionary(HashedCollection)>>findElementOrNil: IdentityDictionary(Dictionary)>>at:ifAbsent: FLPluggableSubstitutionCluster(FLSubstitutionCluster)>>add:traceWith: FLAnalysis>>mapAndTrace: FLAnalysis>>run [:anObject | (FLAnalysis newWith: self firstInMapperChain root: anObject) run; yourself] in FLAnalyzer>>setDefaultAnalysis FLAnalyzer>>analysisFor: FLSerialization>>analysisStep FLSerialization>>run [:anObject :aStream | (FLSerialization root: anObject on: aStream analyzer: self analyzer) run; yourself] in FLSerializer>>defaultSerialization [self nextPutSignatureOn: aStream. self nextPutVersionOn: aStream. ^ self serializationFactory value: anObject value: aStream] in FLSerializer>>serialize:on: BlockClosure>>ensure: FLSerializer>>serialize:on: [:aStream | self serializer serialize: anObject on: aStream] in FLPluggableSubstitutionTest(FLSerializationTest)>>serialize: [:aStream | aValuable value: aStream binary] in FLFileStreamStrategy>>writeStreamDo: [anotherBlock value: file] in MultiByteFileStream class(FileStream class)>>detectFile:do: BlockClosure>>ensure: MultiByteFileStream class(FileStream class)>>detectFile:do: MultiByteFileStream class(FileStream class)>>forceNewFileNamed:do:
Hi Yanni This is a feature or a known issue, I am not sure yet! In serialization, when you substitute an object by another, such substitution(*) is then serialized using the same rules, so you might need the guard condition to avoid the infinite loop. This was the simpler implementation, but maybe it's undesirable or it's too error prone. Do you think we should change this? *: note the substitution can be not just an object but a graph of objects, some of them able to be substituted too. MartÃn On Fri, Nov 25, 2011 at 6:20 PM, Yanni Chiu <yanni@rogers.com> wrote:
The attached test case, reproduced below:
| result | self analyzer when: [:x | FLPair = x class and: [x left isNil not] ] substituteBy: [:x | x copy left: $A ]. result := self resultOfSerializeAndMaterializ**e: (FLPair new left: $a; right: $b; yourself). self assert: result left = $A.
causes an infinite loop with IdentityDictionary (see stack trace at end).
The similar test case #testTransientPairLeft, works because the guard condition is negated by the replacement (i.e. "x left isNil not" is made false by the replacement).
The attached test case does not make "x left isNil not" false. It causes an infinite recursion, so use "ctrl-." to stop it before too long.
Is this a bug, or the intention of the #when:substituteBy: feature? I've worked around it, by making sure to always have the substituted value fail the guard clause.
-- Yanni
==== stack trace ==== IdentityDictionary>>scanFor: IdentityDictionary(**HashedCollection)>>**findElementOrNil: IdentityDictionary(Dictionary)**>>at:ifAbsent: FLPluggableSubstitutionCluster**(FLSubstitutionCluster)>>add:**traceWith: FLAnalysis>>mapAndTrace: FLAnalysis>>run [:anObject | (FLAnalysis newWith: self firstInMapperChain root: anObject) run; yourself] in FLAnalyzer>>setDefaultAnalysis FLAnalyzer>>analysisFor: FLSerialization>>analysisStep FLSerialization>>run [:anObject :aStream | (FLSerialization root: anObject on: aStream analyzer: self analyzer) run; yourself] in FLSerializer>>** defaultSerialization [self nextPutSignatureOn: aStream. self nextPutVersionOn: aStream. ^ self serializationFactory value: anObject value: aStream] in FLSerializer>>serialize:on: BlockClosure>>ensure: FLSerializer>>serialize:on: [:aStream | self serializer serialize: anObject on: aStream] in FLPluggableSubstitutionTest(**FLSerializationTest)>>**serialize: [:aStream | aValuable value: aStream binary] in FLFileStreamStrategy>>** writeStreamDo: [anotherBlock value: file] in MultiByteFileStream class(FileStream class)>>detectFile:do: BlockClosure>>ensure: MultiByteFileStream class(FileStream class)>>detectFile:do: MultiByteFileStream class(FileStream class)>>forceNewFileNamed:do:
On 25/11/11 5:26 PM, Martin Dias wrote:
Hi Yanni
This is a feature or a known issue, I am not sure yet!
It makes sense why it is the way it is (as you've noted below). Once you know this is what's happening. But, when first encountered, it's a bit scary to see your image not respond. I'm not sure whether it's a bug or feature, either. However, I suspect any infinite recursion should be classified as a bug.
In serialization, when you substitute an object by another, such substitution(*) is then serialized using the same rules, so you might need the guard condition to avoid the infinite loop.
This was the simpler implementation, but maybe it's undesirable or it's too error prone. Do you think we should change this?
Probably, the right answer is to allow a choice of whether or not the substituted object(s) are subject to further substitution. And, no matter which choice is made, no infinite loop should occur -- a tall order.
*: note the substitution can be not just an object but a graph of objects, some of them able to be substituted too.
Good to know this.
For this "problem" at least it is documented in http://rmod.lille.inria.fr/web/pier/software/Fuel/Version1.7/Hooks in the section "Substitutes on Serialization" On Fri, Nov 25, 2011 at 8:46 PM, Yanni Chiu <yanni@rogers.com> wrote:
On 25/11/11 5:26 PM, Martin Dias wrote:
Hi Yanni
This is a feature or a known issue, I am not sure yet!
It makes sense why it is the way it is (as you've noted below). Once you know this is what's happening. But, when first encountered, it's a bit scary to see your image not respond. I'm not sure whether it's a bug or feature, either. However, I suspect any infinite recursion should be classified as a bug.
In serialization, when you substitute an object by another, such
substitution(*) is then serialized using the same rules, so you might need the guard condition to avoid the infinite loop.
This was the simpler implementation, but maybe it's undesirable or it's too error prone. Do you think we should change this?
Probably, the right answer is to allow a choice of whether or not the substituted object(s) are subject to further substitution. And, no matter which choice is made, no infinite loop should occur -- a tall order.
*: note the substitution can be not just an object but a graph of
objects, some of them able to be substituted too.
Good to know this.
-- Mariano http://marianopeck.wordpress.com
In serialization, when you substitute an object by another, such
substitution(*) is then serialized using the same rules, so you might need the guard condition to avoid the infinite loop.
This was the simpler implementation, but maybe it's undesirable or it's too error prone. Do you think we should change this?
Probably, the right answer is to allow a choice of whether or not the substituted object(s) are subject to further substitution. And, no matter which choice is made, no infinite loop should occur -- a tall order.
But if the choice made is to let substituted objects to be substituted again, then a loop may happen, and Fuel cannot control/detect that. Maybe the default value should not allow substituting substituted objects?
*: note the substitution can be not just an object but a graph of
objects, some of them able to be substituted too.
Good to know this.
-- Mariano http://marianopeck.wordpress.com
On 27/11/11 8:58 AM, Mariano Martinez Peck wrote:
But if the choice made is to let substituted objects to be substituted again, then a loop may happen, and Fuel cannot control/detect that. Maybe the default value should not allow substituting substituted objects?
I suppose a general solution would be a maximum substitution count, with a default of one. I have no idea, or use case, to know how useful it would be to substitute substituted objects - so just consider this idle ramblings.
participants (3)
-
Mariano Martinez Peck -
Martin Dias -
Yanni Chiu