diff --git a/src/Peachpie.Library/Serialization.cs b/src/Peachpie.Library/Serialization.cs index c63a1137a2..09bca15f7b 100644 --- a/src/Peachpie.Library/Serialization.cs +++ b/src/Peachpie.Library/Serialization.cs @@ -181,7 +181,7 @@ sealed class ObjectWriter : PhpVariableVisitor /// /// Object ID counter used by the r and R tokens. /// - int sequenceNumber; + int _seq; /// /// Maintains a sequence number for every and that have already been serialized. @@ -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); @@ -317,21 +317,20 @@ public override void AcceptArrayItem(KeyValuePair 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); @@ -341,7 +340,7 @@ public override void Accept(PhpAlias value) } else { - serializedRefs.Add(value, sequenceNumber + 1); + serializedRefs[value] = _seq; value.Value.Accept(this); } } @@ -356,6 +355,8 @@ public override void AcceptObject(object value) } else { + ++_seq; + int seq; if (serializedRefs.TryGetValue(value, out seq)) { @@ -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); } } @@ -562,7 +562,6 @@ void AcceptObjectProperties(IEnumerable> 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); } } @@ -578,6 +577,21 @@ internal sealed class ObjectReader readonly Stream _stream; readonly RuntimeTypeHandle _caller; + /// + /// Deserialized objects map. + /// + Dictionary _lazyObjects; + + PhpAlias/*!*/AddSeq() + { + if (_lazyObjects == null) + { + _lazyObjects = new Dictionary(); + } + + return (_lazyObjects[_lazyObjects.Count + 1] = new PhpAlias(PhpValue.Null)); + } + public ObjectReader(Context ctx, Stream stream, RuntimeTypeHandle caller) { Debug.Assert(ctx != null); @@ -625,9 +639,9 @@ private void ThrowInvalidLength() /// /// Throws a due to an invalid back-reference. /// - 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 @@ -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(); @@ -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); @@ -981,6 +997,7 @@ PhpArray ParseArray() Consume(Tokens.BraceClose); // + seq.Value = arr; return arr; } @@ -990,6 +1007,8 @@ PhpArray ParseArray() /// If true, the last token eaten was C, otherwise O. object ParseObject(bool serializable) { + var seq = AddSeq(); + // :{length}:"{classname}": Consume(Tokens.Colon); // : string class_name = ReadString().AsString(); // :"classname" @@ -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