svenvc / ston

STON - Smalltalk Object Notation - A lightweight text-based, human-readable data interchange format for class-based object-oriented languages like Smalltalk.
MIT License
135 stars 32 forks source link

[BUG] - Can not serialize Float infinity #39

Closed Nyan11 closed 1 year ago

Nyan11 commented 1 year ago

Hello,

Description

I was trying to use STON in a personal project (Serialization of Bloc). And i found a bug.

severity: high

Reproduction

code to reproduce:

serialization := STON toString: Float infinity.

materialization := STON fromString: serialization.

current result:

STON fromString: serialization raise an exception but the bug occurs durring the serilization.

STON toString: Float infinity create a String : 'Float infinity'.

Because STON serialize this 'number' as a Class, it will try to materialize it as class. And the exception is raise.

Possible fix

I try to fix it quick by adding a custom method fromSton: in Float class

Float class >> fromSton: aStonReader

    aStonReader match: 'infinity' do: [ ^ self infinity ].
    ^ self nan

BUT, it breaks the STONReference indexation.

Durring serialization := STON toString: Float infinity. the STONWriter doesn't have any reference in the variable named objects.

image

Durring materialize:= STON toString: Float infinity. with the custom fromSton:, the STONReader have the reference of a Float object.

image

This will create errors of indexation when resolving STONReference.

Maybe a solution ?

I have a suggestion to fix the issue.

We could make the Float infinity as constant (like a boolean true or false) when we serialize it.

It would look like something like this:

Float >> stonOn: stonWriter

    self isInfinite
        ifTrue: [ stonWriter writeInfinite ]
        ifFalse: [ stonWriter writeFloat: self asFloat ]
STONWriter >> writeInfinite

    writeStream << #infinite
STONReader >> parseConstantDo: block

    "Custom to include Float infinite"

    readStream peek = $t ifTrue: [ 
        ^ self match: 'true' do: [ block value: true ] ].
    readStream peek = $f ifTrue: [ 
        ^ self match: 'false' do: [ block value: false ] ].
    readStream peek = $i ifTrue: [ 
        self match: 'infinite' do: [ block value: Float infinity ] ].
    readStream peek = $n ifTrue: [ 
        readStream next.
        readStream peek = $i ifTrue: [ 
            self match: 'il' do: [ block value: nil ] ].
        readStream peek = $u ifTrue: [ 
            self match: 'ull' do: [ block value: nil ] ] ]

I don't know if it will work with the JSON serialization.

Other

pharo image : Pharo-11.0.0+build.346.sha.b6ce4527b4f4881d6c6b4f968f1d1348b783b7a2 (64 Bit)

svenvc commented 1 year ago

Are you sure you are using the latest version of STON ?

In any case, it works in the latest version:

STON fromString: (STON toString: { Float infinity. Float nan. Float negativeInfinity }). 

"{Float infinity. Float nan. Float infinity negated}"

The actual representation being:

[Float[#infinity],Float[#nan],Float[#negativeInfinity]]

This is also described in the spec: https://github.com/svenvc/ston/blob/master/ston-spec.md in the Float section and is tested by several unit tests.

Can you confirm this ?

svenvc commented 1 year ago

This functionality was added last summer https://github.com/svenvc/ston/commit/a9959114c3036aad1dd5c327a5b8ac3309e4c3e5

Nyan11 commented 1 year ago

Hello,

sorry to have take so long to respond. You are right, I wasn't on the correct version of STON (I was using the one provided by default on the Pharo image).

But, i think the serialization is still not possible with Float infinity. Here is the code to reproduce the bug about the breaking of STONReference.

Reproduction

code to reproduce:

something := Object new.
array1 := { Float infinity . something . something }.
string := STON toString: array1.
array2 := STON fromString: string.

current result (when you inspect array1 and array2):

array1 >> 'an Array(Float infinity an Object an Object)'
array2 >> 'an Array(Float infinity an Object Float infinity)'

In this example, the last object of the array is different between array1 and array2. It should be the object represented by the variable "something"(an Object).

expected result:

array1 >> 'an Array(Float infinity an Object an Object)'
array2 >> 'an Array(Float infinity an Object an Object)'

Other

pharo image: Pharo-11.0.0+build.346.sha.b6ce4527b4f4881d6c6b4f968f1d1348b783b7a2 (64 Bit) ston version: 96de626 (wich include https://github.com/svenvc/ston/commit/a9959114c3036aad1dd5c327a5b8ac3309e4c3e5)

svenvc commented 1 year ago

Ah that is a deep issue with the internal reference protocol. Thanks for bringing that forward!

Fixed in https://github.com/svenvc/ston/commit/89c7e5984210a36f79a0ad3272d8b29a17793032

Here was my test case:

something := Color red.
array1 := { Float infinity . something . something }.
string := STON toString: array1.
array2 := STON fromString: string.
array1 = array2.
array2 second == array2 third.

Both tests should be true.

svenvc commented 1 year ago

Ah, I noticed that I did not properly add your name the commit (I was doing two things at once). Sorry.

But thanks again!

Nyan11 commented 1 year ago

Hello, i test it on my project and it works well.

Thank you !

svenvc commented 1 year ago

Thank for the feedback, this is really important to find edge cases.