Skip to content

Commit

Permalink
serialize/unserialize refs (token r|R)
Browse files Browse the repository at this point in the history
  • Loading branch information
jakubmisek committed Mar 31, 2019
1 parent e6c3f0e commit a3e6168
Showing 1 changed file with 53 additions and 15 deletions.
68 changes: 53 additions & 15 deletions src/Peachpie.Library/Serialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ sealed class ObjectWriter : PhpVariableVisitor
/// <summary>
/// Object ID counter used by the <B>r</B> and <B>R</B> tokens.
/// </summary>
int sequenceNumber;
int _seq;

/// <summary>
/// Maintains a sequence number for every <see cref="object"/> and <see cref="PhpAlias"/> that have already been serialized.
Expand Down Expand Up @@ -294,7 +294,7 @@ public override void Accept(PhpString obj)

public override void Accept(PhpArray value)
{
serializedRefs[value] = sequenceNumber;
serializedRefs[value] = ++_seq;

Write(Tokens.Array);
Write(Tokens.Colon);
Expand All @@ -317,21 +317,20 @@ public override void AcceptArrayItem(KeyValuePair<IntStringKey, PhpValue> entry)
Accept(entry.Key.String);

// value
sequenceNumber--; // don't assign a seq number to array keys
entry.Value.Accept(this);
}

public override void Accept(PhpAlias value)
{
sequenceNumber--;
++_seq;

if (value.ReferenceCount == 0)
{
value.Value.Accept(this);
}
else
{
int seq;
if (serializedRefs.TryGetValue(value, out seq))
if (serializedRefs.TryGetValue(value, out var seq))
{
// this reference has already been serialized -> write out its seq. number
Write(Tokens.Reference);
Expand All @@ -341,7 +340,7 @@ public override void Accept(PhpAlias value)
}
else
{
serializedRefs.Add(value, sequenceNumber + 1);
serializedRefs[value] = _seq;
value.Value.Accept(this);
}
}
Expand All @@ -356,6 +355,8 @@ public override void AcceptObject(object value)
}
else
{
++_seq;

int seq;
if (serializedRefs.TryGetValue(value, out seq))
{
Expand All @@ -364,11 +365,10 @@ public override void AcceptObject(object value)
Write(Tokens.Colon);
Write(seq.ToString());
Write(Tokens.Semicolon);
sequenceNumber--;
}
else
{
serializedRefs.Add(value, sequenceNumber);
serializedRefs[value] = _seq;
SerializeObject(value);
}
}
Expand Down Expand Up @@ -562,7 +562,6 @@ void AcceptObjectProperties(IEnumerable<KeyValuePair<string, PhpValue>> properti
{
// write out the property name and the property value
Accept(pair.Key);
sequenceNumber--; // don't assign a seq number to property names
Accept(pair.Value);
}
}
Expand All @@ -578,6 +577,21 @@ internal sealed class ObjectReader
readonly Stream _stream;
readonly RuntimeTypeHandle _caller;

/// <summary>
/// Deserialized objects map.
/// </summary>
Dictionary<int, PhpAlias> _lazyObjects;

PhpAlias/*!*/AddSeq()
{
if (_lazyObjects == null)
{
_lazyObjects = new Dictionary<int, PhpAlias>();
}

return (_lazyObjects[_lazyObjects.Count + 1] = new PhpAlias(PhpValue.Null));
}

public ObjectReader(Context ctx, Stream stream, RuntimeTypeHandle caller)
{
Debug.Assert(ctx != null);
Expand Down Expand Up @@ -625,9 +639,9 @@ private void ThrowInvalidLength()
/// <summary>
/// Throws a <see cref="SerializationException"/> due to an invalid back-reference.
/// </summary>
private void ThrowInvalidReference()
private Exception InvalidReferenceException()
{
throw new InvalidDataException(LibResources.invalid_data_bad_back_reference);
return new InvalidDataException(LibResources.invalid_data_bad_back_reference);
}

#endregion
Expand Down Expand Up @@ -828,8 +842,8 @@ PhpValue Parse()
case Tokens.Array: return PhpValue.Create(ParseArray());
case Tokens.Object: return PhpValue.FromClass(ParseObject(false));
case Tokens.ObjectSer: return PhpValue.FromClass(ParseObject(true));
case Tokens.Reference: throw new NotImplementedException();
case Tokens.ObjectRef: throw new NotImplementedException();
case Tokens.Reference: return ParseObjectRef();
case Tokens.ObjectRef: return ParseObjectRef();

default:
ThrowUnexpected();
Expand Down Expand Up @@ -953,11 +967,13 @@ PhpValue ParseString()

PhpArray ParseArray()
{
var seq = AddSeq();

Consume(Tokens.Colon);
int length = unchecked((int)ReadInteger());
if (length < 0) ThrowInvalidLength();

var arr = (length == 0) ? PhpArray.NewEmpty() : new PhpArray(length);
var arr = new PhpArray(length);

Consume(Tokens.Colon);
Consume(Tokens.BraceOpen);
Expand All @@ -981,6 +997,7 @@ PhpArray ParseArray()
Consume(Tokens.BraceClose);

//
seq.Value = arr;
return arr;
}

Expand All @@ -990,6 +1007,8 @@ PhpArray ParseArray()
/// <param name="serializable">If <B>true</B>, the last token eaten was <B>C</B>, otherwise <B>O</B>.</param>
object ParseObject(bool serializable)
{
var seq = AddSeq();

// :{length}:"{classname}":
Consume(Tokens.Colon); // :
string class_name = ReadString().AsString(); // <length>:"classname"
Expand Down Expand Up @@ -1081,8 +1100,27 @@ object ParseObject(bool serializable)
Consume(Tokens.BraceClose);

//
seq.Value = PhpValue.FromClass(obj);
return obj;
}

PhpAlias ParseObjectRef()
{
var seq = AddSeq();

Consume(Tokens.Colon);

var seqref = (int)ReadInteger();
if (_lazyObjects == null || !_lazyObjects.TryGetValue(seqref, out var alias))
{
throw InvalidReferenceException();
}

Consume(Tokens.Semicolon);

//
return alias;
}
}

#endregion
Expand Down

0 comments on commit a3e6168

Please # to comment.