@@ -124,71 +124,87 @@ internal void Merge(IMessage message, TextReader jsonReader)
124124 /// of tokens provided by the tokenizer. This token stream is assumed to be valid JSON, with the
125125 /// tokenizer performing that validation - but not every token stream is valid "protobuf JSON".
126126 /// </summary>
127+ /// <remarks>
128+ /// This method maintains and checks the recursion depth, so *must* be called for any nested parsing.
129+ /// </remarks>
127130 private void Merge ( IMessage message , JsonTokenizer tokenizer )
128131 {
129- if ( tokenizer . ObjectDepth > settings . RecursionLimit )
132+ if ( tokenizer . RecursionDepth > settings . RecursionLimit )
130133 {
131134 throw InvalidProtocolBufferException . JsonRecursionLimitExceeded ( ) ;
132135 }
133- if ( message . Descriptor . IsWellKnownType )
134- {
135- if ( WellKnownTypeHandlers . TryGetValue ( message . Descriptor . FullName , out Action < JsonParser , IMessage , JsonTokenizer > handler ) )
136- {
137- handler ( this , message , tokenizer ) ;
138- return ;
139- }
140- // Well-known types with no special handling continue in the normal way.
141- }
142- var token = tokenizer . Next ( ) ;
143- if ( token . Type != JsonToken . TokenType . StartObject )
144- {
145- throw new InvalidProtocolBufferException ( "Expected an object" ) ;
146- }
147- var descriptor = message . Descriptor ;
148- var jsonFieldMap = descriptor . Fields . ByJsonName ( ) ;
149- // All the oneof fields we've already accounted for - we can only see each of them once.
150- // The set is created lazily to avoid the overhead of creating a set for every message
151- // we parsed, when oneofs are relatively rare.
152- HashSet < OneofDescriptor > seenOneofs = null ;
153- while ( true )
136+ tokenizer . RecursionDepth ++ ;
137+
138+ // try/finally used in order to decrement the recursion depth regardless of outcome.
139+ // If an exception is thrown, the recursion depth is irrelevant anyway - but as the method
140+ // has multiple return statements, this is the simplest way of ensuring the recursion depth
141+ // is always decremented. An alternative would be to use a local function.
142+ try
154143 {
155- token = tokenizer . Next ( ) ;
156- if ( token . Type == JsonToken . TokenType . EndObject )
144+ if ( message . Descriptor . IsWellKnownType )
157145 {
158- return ;
146+ if ( WellKnownTypeHandlers . TryGetValue ( message . Descriptor . FullName , out Action < JsonParser , IMessage , JsonTokenizer > handler ) )
147+ {
148+ handler ( this , message , tokenizer ) ;
149+ return ;
150+ }
151+ // Well-known types with no special handling continue in the normal way.
159152 }
160- if ( token . Type != JsonToken . TokenType . Name )
153+ var token = tokenizer . Next ( ) ;
154+ if ( token . Type != JsonToken . TokenType . StartObject )
161155 {
162- throw new InvalidOperationException ( "Unexpected token type " + token . Type ) ;
156+ throw new InvalidProtocolBufferException ( "Expected an object" ) ;
163157 }
164- string name = token . StringValue ;
165- if ( jsonFieldMap . TryGetValue ( name , out FieldDescriptor field ) )
158+ var descriptor = message . Descriptor ;
159+ var jsonFieldMap = descriptor . Fields . ByJsonName ( ) ;
160+ // All the oneof fields we've already accounted for - we can only see each of them once.
161+ // The set is created lazily to avoid the overhead of creating a set for every message
162+ // we parsed, when oneofs are relatively rare.
163+ HashSet < OneofDescriptor > seenOneofs = null ;
164+ while ( true )
166165 {
167- if ( field . ContainingOneof != null )
166+ token = tokenizer . Next ( ) ;
167+ if ( token . Type == JsonToken . TokenType . EndObject )
168168 {
169- if ( seenOneofs == null )
170- {
171- seenOneofs = new HashSet < OneofDescriptor > ( ) ;
172- }
173- if ( ! seenOneofs . Add ( field . ContainingOneof ) )
174- {
175- throw new InvalidProtocolBufferException ( $ "Multiple values specified for oneof { field . ContainingOneof . Name } ") ;
176- }
169+ return ;
177170 }
178- MergeField ( message , field , tokenizer ) ;
179- }
180- else
181- {
182- if ( settings . IgnoreUnknownFields )
171+ if ( token . Type != JsonToken . TokenType . Name )
172+ {
173+ throw new InvalidOperationException ( "Unexpected token type " + token . Type ) ;
174+ }
175+ string name = token . StringValue ;
176+ if ( jsonFieldMap . TryGetValue ( name , out FieldDescriptor field ) )
183177 {
184- tokenizer . SkipValue ( ) ;
178+ if ( field . ContainingOneof != null )
179+ {
180+ if ( seenOneofs == null )
181+ {
182+ seenOneofs = new HashSet < OneofDescriptor > ( ) ;
183+ }
184+ if ( ! seenOneofs . Add ( field . ContainingOneof ) )
185+ {
186+ throw new InvalidProtocolBufferException ( $ "Multiple values specified for oneof { field . ContainingOneof . Name } ") ;
187+ }
188+ }
189+ MergeField ( message , field , tokenizer ) ;
185190 }
186191 else
187192 {
188- throw new InvalidProtocolBufferException ( "Unknown field: " + name ) ;
193+ if ( settings . IgnoreUnknownFields )
194+ {
195+ tokenizer . SkipValue ( ) ;
196+ }
197+ else
198+ {
199+ throw new InvalidProtocolBufferException ( "Unknown field: " + name ) ;
200+ }
189201 }
190202 }
191203 }
204+ finally
205+ {
206+ tokenizer . RecursionDepth -- ;
207+ }
192208 }
193209
194210 private void MergeField ( IMessage message , FieldDescriptor field , JsonTokenizer tokenizer )
0 commit comments