Merge pull request #496 from nicolas-raoul/unit-test-for-issue2907
[mono.git] / mcs / class / System.Web.Extensions / System.Web.Script.Serialization / JsonDeserializer.cs
1 //
2 // JsonDeserializer.cs
3 //
4 // Author:
5 //   Marek Habersack <grendel@twistedcode.net>
6 //
7 // (C) 2008 Novell, Inc.  http://novell.com/
8 // Copyright 2011, Xamarin, Inc (http://xamarin.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 // Code is based on JSON_checker (http://www.json.org/JSON_checker/) and JSON_parser
30 // (http://fara.cs.uni-potsdam.de/~jsg/json_parser/) C sources.
31
32 using System;
33 using System.Collections;
34 using System.Collections.Generic;
35 using System.Data;
36 using System.Globalization;
37 using System.IO;
38 using System.Reflection;
39 using System.Text;
40
41 namespace System.Web.Script.Serialization
42 {
43         internal sealed class JsonDeserializer
44         {
45                 /* Universal error constant */
46                 const int __ = -1;
47                 const int UNIVERSAL_ERROR = __;
48                 
49                 /*
50                   Characters are mapped into these 31 character classes. This allows for
51                   a significant reduction in the size of the state transition table.
52                 */
53                 const int C_SPACE = 0x00;  /* space */
54                 const int C_WHITE = 0x01;  /* other whitespace */
55                 const int C_LCURB = 0x02;  /* {  */
56                 const int C_RCURB = 0x03;  /* } */
57                 const int C_LSQRB = 0x04;  /* [ */
58                 const int C_RSQRB = 0x05;  /* ] */
59                 const int C_COLON = 0x06;  /* : */
60                 const int C_COMMA = 0x07;  /* , */
61                 const int C_QUOTE = 0x08;  /* " */
62                 const int C_BACKS = 0x09;  /* \ */
63                 const int C_SLASH = 0x0A;  /* / */
64                 const int C_PLUS  = 0x0B;  /* + */
65                 const int C_MINUS = 0x0C;  /* - */
66                 const int C_POINT = 0x0D;  /* . */
67                 const int C_ZERO  = 0x0E;  /* 0 */
68                 const int C_DIGIT = 0x0F;  /* 123456789 */
69                 const int C_LOW_A = 0x10;  /* a */
70                 const int C_LOW_B = 0x11;  /* b */
71                 const int C_LOW_C = 0x12;  /* c */
72                 const int C_LOW_D = 0x13;  /* d */
73                 const int C_LOW_E = 0x14;  /* e */
74                 const int C_LOW_F = 0x15;  /* f */
75                 const int C_LOW_L = 0x16;  /* l */
76                 const int C_LOW_N = 0x17;  /* n */
77                 const int C_LOW_R = 0x18;  /* r */
78                 const int C_LOW_S = 0x19;  /* s */
79                 const int C_LOW_T = 0x1A;  /* t */
80                 const int C_LOW_U = 0x1B;  /* u */
81                 const int C_ABCDF = 0x1C;  /* ABCDF */
82                 const int C_E     = 0x1D;  /* E */
83                 const int C_ETC   = 0x1E;  /* everything else */
84                 const int C_STAR  = 0x1F;  /* * */
85                 const int C_I     = 0x20;  /* I */
86                 const int C_LOW_I = 0x21;  /* i */
87                 const int C_LOW_Y = 0x22;  /* y */
88                 const int C_N     = 0x23;  /* N */
89                 
90                 /* The state codes. */
91                 const int GO = 0x00;  /* start    */
92                 const int OK = 0x01;  /* ok       */
93                 const int OB = 0x02;  /* object   */
94                 const int KE = 0x03;  /* key      */
95                 const int CO = 0x04;  /* colon    */
96                 const int VA = 0x05;  /* value    */
97                 const int AR = 0x06;  /* array    */
98                 const int ST = 0x07;  /* string   */
99                 const int ES = 0x08;  /* escape   */
100                 const int U1 = 0x09;  /* u1       */
101                 const int U2 = 0x0A;  /* u2       */
102                 const int U3 = 0x0B;  /* u3       */
103                 const int U4 = 0x0C;  /* u4       */
104                 const int MI = 0x0D;  /* minus    */
105                 const int ZE = 0x0E;  /* zero     */
106                 const int IN = 0x0F;  /* integer  */
107                 const int FR = 0x10;  /* fraction */
108                 const int E1 = 0x11;  /* e        */
109                 const int E2 = 0x12;  /* ex       */
110                 const int E3 = 0x13;  /* exp      */
111                 const int T1 = 0x14;  /* tr       */
112                 const int T2 = 0x15;  /* tru      */
113                 const int T3 = 0x16;  /* true     */
114                 const int F1 = 0x17;  /* fa       */
115                 const int F2 = 0x18;  /* fal      */
116                 const int F3 = 0x19;  /* fals     */
117                 const int F4 = 0x1A;  /* false    */
118                 const int N1 = 0x1B;  /* nu       */
119                 const int N2 = 0x1C;  /* nul      */
120                 const int N3 = 0x1D;  /* null     */
121                 const int FX = 0x1E;  /* *.* *eE* */
122                 const int IV = 0x1F;  /* invalid input */
123                 const int UK = 0x20;  /* unquoted key name */
124                 const int UI = 0x21; /* ignore during unquoted key name construction */
125                 const int I1 = 0x22;  /* In */
126                 const int I2 = 0x23;  /* Inf */
127                 const int I3 = 0x24;  /* Infi */
128                 const int I4 = 0x25;  /* Infin */
129                 const int I5 = 0x26;  /* Infini */
130                 const int I6 = 0x27;  /* Infinit */
131                 const int I7 = 0x28;  /* Infinity */
132                 const int V1 = 0x29;  /* Na */
133                 const int V2 = 0x2A;  /* NaN */
134                 
135                 /* Actions */
136                 const int FA = -10; /* false */
137                 const int TR = -11; /* false */
138                 const int NU = -12; /* null */
139                 const int DE = -13; /* double detected by exponent e E */
140                 const int DF = -14; /* double detected by fraction . */
141                 const int SB = -15; /* string begin */
142                 const int MX = -16; /* integer detected by minus */
143                 const int ZX = -17; /* integer detected by zero */
144                 const int IX = -18; /* integer detected by 1-9 */
145                 const int EX = -19; /* next char is escaped */
146                 const int UC = -20; /* Unicode character read */
147                 const int SE =  -4; /* string end */
148                 const int AB =  -5; /* array begin */
149                 const int AE =  -7; /* array end */
150                 const int OS =  -6; /* object start */
151                 const int OE =  -8; /* object end */
152                 const int EO =  -9; /* empty object */
153                 const int CM =  -3; /* comma */
154                 const int CA =  -2; /* colon action */
155                 const int PX = -21; /* integer detected by plus */
156                 const int KB = -22; /* unquoted key name begin */
157                 const int UE = -23; /* unquoted key name end */
158                 const int IF = -25; /* Infinity */
159                 const int NN = -26; /* NaN */
160                 
161                 
162                 enum JsonMode {
163                         NONE,
164                         ARRAY,
165                         DONE,
166                         KEY,
167                         OBJECT
168                 };
169
170                 enum JsonType {
171                         NONE = 0,
172                         ARRAY_BEGIN,
173                         ARRAY_END,
174                         OBJECT_BEGIN,
175                         OBJECT_END,
176                         INTEGER,
177                         FLOAT,
178                         NULL,
179                         TRUE,
180                         FALSE,
181                         STRING,
182                         KEY,
183                         MAX
184                 };
185                 
186                 /*
187                   This array maps the 128 ASCII characters into character classes.
188                   The remaining Unicode characters should be mapped to C_ETC.
189                   Non-whitespace control characters are errors.
190                 */
191                 static readonly int[] ascii_class = {
192                         __,      __,      __,      __,      __,      __,      __,      __,
193                         __,      C_WHITE, C_WHITE, __,      __,      C_WHITE, __,      __,
194                         __,      __,      __,      __,      __,      __,      __,      __,
195                         __,      __,      __,      __,      __,      __,      __,      __,
196
197                         C_SPACE, C_ETC,   C_QUOTE, C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_QUOTE,
198                         C_ETC,   C_ETC,   C_STAR,   C_PLUS,  C_COMMA, C_MINUS, C_POINT, C_SLASH,
199                         C_ZERO,  C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
200                         C_DIGIT, C_DIGIT, C_COLON, C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
201
202                         C_ETC,   C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E,     C_ABCDF, C_ETC,
203                         C_ETC,   C_I,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_N,   C_ETC,
204                         C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
205                         C_ETC,   C_ETC,   C_ETC,   C_LSQRB, C_BACKS, C_RSQRB, C_ETC,   C_ETC,
206
207                         C_ETC,   C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC,
208                         C_ETC,   C_LOW_I, C_ETC,   C_ETC,   C_LOW_L, C_ETC,   C_LOW_N, C_ETC,
209                         C_ETC,   C_ETC,   C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC,   C_ETC,
210                         C_ETC,   C_LOW_Y,   C_ETC,   C_LCURB, C_ETC,   C_RCURB, C_ETC,   C_ETC
211                 };
212
213                 static readonly int[,] state_transition_table = {
214                 /*
215                   The state transition table takes the current state and the current symbol,
216                   and returns either a new state or an action. An action is represented as a
217                   negative number. A JSON text is accepted if at the end of the text the
218                   state is OK and if the mode is MODE_DONE.
219
220                                            white                  '                   1-9                                   ABCDF  etc
221                                        space |  {  }  [  ]  :  ,  "  \  /  +  -  .  0  |  a  b  c  d  e  f  l  n  r  s  t  u  |  E  |  *  I  i  y  N */
222                         /*start    GO*/ {GO,GO,OS,__,AB,__,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,__,__,__,TR,__,__,__,__,__,I1,__,__,V1},
223                         /*ok       OK*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
224                         /*object   OB*/ {OB,OB,__,EO,__,__,__,__,SB,__,__,__,KB,__,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,__,KB,KB,KB,KB},
225                         /*key      KE*/ {KE,KE,__,__,__,__,__,__,SB,__,__,__,KB,__,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,KB,__,KB,KB,KB,KB},
226                         /*colon    CO*/ {CO,CO,__,__,__,__,CA,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
227                         /*value    VA*/ {VA,VA,OS,__,AB,__,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__,I1,__,__,V1},
228                         /*array    AR*/ {AR,AR,OS,__,AB,AE,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__,I1,__,__,V1},
229                         /*string   ST*/ {ST,__,ST,ST,ST,ST,ST,ST,SE,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST},
230                         /*escape   ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__,__,__,__,__,__},
231                         /*u1       U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__,__,__,__,__,__},
232                         /*u2       U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__,__,__,__,__,__},
233                         /*u3       U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__,__,__,__,__,__},
234                         /*u4       U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,UC,UC,UC,UC,UC,UC,UC,UC,__,__,__,__,__,__,UC,UC,__,__,__,__,__,__},
235                         /*minus    MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IN,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I1,__,__,__},
236                         /*zero     ZE*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,DF,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
237                         /*int      IN*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,DF,IN,IN,__,__,__,__,DE,__,__,__,__,__,__,__,__,DE,__,__,__,__,__,__},
238                         /*frac     FR*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__,__,__,__,__},
239                         /*e        E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
240                         /*ex       E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
241                         /*exp      E3*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
242                         /*tr       T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__,__,__,__,__,__},
243                         /*tru      T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__,__,__,__,__,__},
244                         /*true     T3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
245                         /*fa       F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
246                         /*fal      F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__,__,__,__,__,__},
247                         /*fals     F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__,__,__,__,__,__},
248                         /*false    F4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
249                         /*nu       N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__,__,__,__,__,__},
250                         /*nul      N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__,__,__,__,__,__},
251                         /*null     N3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__},
252                         /*_.       FX*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__,__,__,__,__},
253                         /*inval.   IV*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
254                         /*unq.key  UK*/ {UI,UI,__,__,__,__,UE,__,__,__,__,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,UK,__,UK,UK,UK,UK},
255                         /*unq.ign. UI*/ {UI,UI,__,__,__,__,UE,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
256                         /*i1       I1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I2,__,__,__,__,__,__,__,__,__,__,__,__},
257                         /*i2       I2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I3,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
258                         /*i3       I3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I4,__,__},
259                         /*i4       I4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I5,__,__,__,__,__,__,__,__,__,__,__,__},
260                         /*i5       I5*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I6,__,__},
261                         /*i6       I6*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I7,__,__,__,__,__,__,__,__,__},
262                         /*i7       I7*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,IF,__},
263                         /*v1       V1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,V2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
264                         /*v2       V2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,NN},
265                 };
266
267                 JavaScriptSerializer serializer;
268                 JavaScriptTypeResolver typeResolver;
269                 int maxJsonLength;
270                 int currentPosition;
271                 int recursionLimit;
272                 int recursionDepth;
273                 
274                 Stack <JsonMode> modes;
275                 Stack <object> returnValue;
276                 JsonType jsonType;
277                 
278                 bool escaped;
279                 int state;
280                 Stack <string> currentKey;
281                 StringBuilder buffer;
282                 char quoteChar;
283                 
284                 public JsonDeserializer (JavaScriptSerializer serializer)
285                 {
286                         this.serializer = serializer;
287                         this.maxJsonLength = serializer.MaxJsonLength;
288                         this.recursionLimit = serializer.RecursionLimit;
289                         this.typeResolver = serializer.TypeResolver;
290                         this.modes = new Stack <JsonMode> ();
291                         this.currentKey = new Stack <string> ();
292                         this.returnValue = new Stack <object> ();
293                         this.state = GO;
294                         this.currentPosition = 0;
295                         this.recursionDepth = 0;
296                 }
297
298                 public object Deserialize (string input)
299                 {
300                         if (input == null)
301                                 throw new ArgumentNullException ("input");
302
303                         return Deserialize (new StringReader (input));
304                 }
305                 
306                 public object Deserialize (TextReader input)
307                 {
308                         if (input == null)
309                                 throw new ArgumentNullException ("input");
310                         
311                         int value;
312                         buffer = new StringBuilder ();
313                         
314                         while (true) {
315                                 value = input.Read ();
316                                 if (value < 0)
317                                         break;
318
319                                 currentPosition++;
320                                 if (currentPosition > maxJsonLength)
321                                         throw new ArgumentException ("Maximum JSON input length has been exceeded.");
322
323                                 if (!ProcessCharacter ((char) value))
324                                         throw new InvalidOperationException ("JSON syntax error.");
325                         }
326
327                         object topObject = PeekObject ();
328                         if (buffer.Length > 0) {
329                                 object result;
330
331                                 if (ParseBuffer (out result)) {
332                                         if (topObject != null)
333                                                 StoreValue (result);
334                                         else
335                                                 PushObject (result);
336                                 }
337                         }
338
339                         if (returnValue.Count > 1)
340                                 throw new InvalidOperationException ("JSON syntax error.");
341
342                         object ret = PopObject ();
343                         return ret;
344                 }
345
346 #if DEBUG
347                 void DumpObject (string indent, object obj)
348                 {
349                         if (obj is Dictionary <string, object>) {
350                                 Console.WriteLine (indent + "{");
351                                 foreach (KeyValuePair <string, object> kvp in (Dictionary <string, object>)obj) {
352                                         Console.WriteLine (indent + "\t\"{0}\": ", kvp.Key);
353                                         DumpObject (indent + "\t\t", kvp.Value);
354                                 }
355                                 Console.WriteLine (indent + "}");
356                         } else if (obj is object[]) {
357                                 Console.WriteLine (indent + "[");
358                                 foreach (object o in (object[])obj)
359                                         DumpObject (indent + "\t", o);
360                                 Console.WriteLine (indent + "]");
361                         } else if (obj != null)
362                                 Console.WriteLine (indent + obj.ToString ());
363                         else
364                                 Console.WriteLine ("null");
365                 }
366 #endif
367                 
368                 void DecodeUnicodeChar ()
369                 {
370                         int len = buffer.Length;
371                         if (len < 6)
372                                 throw new ArgumentException ("Invalid escaped unicode character specification (" + currentPosition + ")");
373
374                         int code = Int32.Parse (buffer.ToString ().Substring (len - 4), NumberStyles.HexNumber);
375                         buffer.Length = len - 6;
376                         buffer.Append ((char)code);
377                 }
378
379                 string GetModeMessage (JsonMode expectedMode)
380                 {
381                         switch (expectedMode) {
382                                 case JsonMode.ARRAY:
383                                         return "Invalid array passed in, ',' or ']' expected (" + currentPosition + ")";
384
385                                 case JsonMode.KEY:
386                                         return "Invalid object passed in, key name or ':' expected (" + currentPosition + ")";
387
388                                 case JsonMode.OBJECT:
389                                         return "Invalid object passed in, key value expected (" + currentPosition + ")";
390                                         
391                                 default:
392                                         return "Invalid JSON string";
393                         }
394                 }
395                 
396                 void PopMode (JsonMode expectedMode)
397                 {
398                         JsonMode mode = PeekMode ();
399                         if (mode != expectedMode)
400                                 throw new ArgumentException (GetModeMessage (mode));
401
402                         modes.Pop ();
403                 }
404
405                 void PushMode (JsonMode newMode)
406                 {
407                         modes.Push (newMode);
408                 }
409
410                 JsonMode PeekMode ()
411                 {
412                         if (modes.Count == 0)
413                                 return JsonMode.NONE;
414
415                         return modes.Peek ();
416                 }
417
418                 void PushObject (object o)
419                 {
420                         returnValue.Push (o);
421                 }
422
423                 object PopObject (bool notIfLast)
424                 {
425                         int count = returnValue.Count;
426                         if (count == 0)
427                                 return null;
428
429                         if (notIfLast && count == 1)
430                                 return null;
431                         
432                         return returnValue.Pop ();
433                 }
434
435                 object PopObject ()
436                 {
437                         return PopObject (false);
438                 }
439                 
440                 object PeekObject ()
441                 {
442                         if (returnValue.Count == 0)
443                                 return null;
444
445                         return returnValue.Peek ();
446                 }
447                 
448                 void RemoveLastCharFromBuffer ()
449                 {
450                         int len = buffer.Length;
451                         if (len == 0)
452                                 return;
453                         buffer.Length = len - 1;
454                 }
455
456                 bool ParseBuffer (out object result)
457                 {
458                         result = null;
459                         
460                         if (jsonType == JsonType.NONE) {
461                                 buffer.Length = 0;
462                                 return false;
463                         }
464
465                         string s = buffer.ToString ();
466                         bool converted = true;
467                         int intValue;
468                         long longValue;
469                         decimal decimalValue;
470                         double doubleValue;
471                         
472                         switch (jsonType) {
473                                 case JsonType.INTEGER:
474                                         /* MS AJAX.NET JSON parser promotes big integers to double */
475                                         
476                                         if (Int32.TryParse (s, out intValue))
477                                                 result = intValue;
478                                         else if (Int64.TryParse (s, out longValue))
479                                                 result = longValue;
480                                         else if (Decimal.TryParse (s, out decimalValue))
481                                                 result = decimalValue;
482                                         else if (Double.TryParse (s, out doubleValue))
483                                                 result = doubleValue;
484                                         else
485                                                 converted = false;
486                                         break;
487
488                                 case JsonType.FLOAT:
489                                         if (Decimal.TryParse (s, out decimalValue))
490                                                 result = decimalValue;
491                                         else if (Double.TryParse (s, out doubleValue))
492                                                 result = doubleValue;
493                                         else
494                                                 converted = false;
495                                         break;
496
497                                 case JsonType.TRUE:
498                                         if (String.Compare (s, "true", StringComparison.Ordinal) == 0)
499                                                 result = true;
500                                         else
501                                                 converted = false;
502                                         break;
503
504                                 case JsonType.FALSE:
505                                         if (String.Compare (s, "false", StringComparison.Ordinal) == 0)
506                                                 result = false;
507                                         else
508                                                 converted = false;
509                                         break;
510
511                                 case JsonType.NULL:
512                                         if (String.Compare (s, "null", StringComparison.Ordinal) != 0)
513                                                 converted = false;
514                                         break;
515
516                                 case JsonType.STRING:
517                                         if (s.StartsWith ("/Date(", StringComparison.Ordinal) && s.EndsWith (")/", StringComparison.Ordinal)) {
518                                                 long javaScriptTicks = Convert.ToInt64 (s.Substring (6, s.Length - 8));
519                                                 result = new DateTime ((javaScriptTicks * 10000) + JsonSerializer.InitialJavaScriptDateTicks, DateTimeKind.Utc);
520                                         } else
521                                                 result = s;
522                                         break;
523
524                                 default:
525                                         throw new InvalidOperationException (String.Format ("Internal error: unexpected JsonType ({0})", jsonType));
526                                         
527                         }
528
529                         if (!converted)
530                                 throw new ArgumentException ("Invalid JSON primitive: " + s);
531
532                         buffer.Length = 0;
533                         return true;
534                 }
535                 
536                 bool ProcessCharacter (char ch)
537                 {
538                         int next_class, next_state;
539
540                         if (ch >= 128)
541                                 next_class = C_ETC;
542                         else {
543                                 next_class = ascii_class [ch];
544                                 if (next_class <= UNIVERSAL_ERROR)
545                                         return false;
546                         }
547
548                         if (escaped) {
549                                 escaped = false;
550                                 RemoveLastCharFromBuffer ();
551                                 
552                                 switch (ch) {
553                                         case 'b':
554                                                 buffer.Append ('\b');
555                                                 break;
556                                         case 'f':
557                                                 buffer.Append ('\f');
558                                                 break;
559                                         case 'n':
560                                                 buffer.Append ('\n');
561                                                 break;
562                                         case 'r':
563                                                 buffer.Append ('\r');
564                                                 break;
565                                         case 't':
566                                                 buffer.Append ('\t');
567                                                 break;
568                                         case '"':
569                                         case '\\':
570                                         case '/':
571                                                 buffer.Append (ch);
572                                                 break;
573                                         case 'u':
574                                                 buffer.Append ("\\u");
575                                                 break;
576                                         default:
577                                                 return false;
578                                 }
579                         } else if (jsonType != JsonType.NONE || !(next_class == C_SPACE || next_class == C_WHITE))
580                                 buffer.Append (ch);
581
582                         next_state = state_transition_table [state, next_class];
583                         if (next_state >= 0) {
584                                 state = next_state;
585                                 return true;
586                         }
587
588                         object result;
589                         /* An action to perform */
590                         switch (next_state) {
591                                 case UC: /* Unicode character */
592                                         DecodeUnicodeChar ();
593                                         state = ST;
594                                         break;
595                                         
596                                 case EX: /* Escaped character */
597                                         escaped = true;
598                                         state = ES;
599                                         break;
600
601                                 case MX: /* integer detected by minus */
602                                         jsonType = JsonType.INTEGER;
603                                         state = MI;
604                                         break;
605
606                                 case PX: /* integer detected by plus */
607                                         jsonType = JsonType.INTEGER;
608                                         state = MI;
609                                         break;
610                                         
611                                 case ZX: /* integer detected by zero */
612                                         jsonType = JsonType.INTEGER;
613                                         state = ZE;
614                                         break;
615
616                                 case IX: /* integer detected by 1-9 */
617                                         jsonType = JsonType.INTEGER;
618                                         state = IN;
619                                         break;
620
621                                 case DE: /* floating point number detected by exponent*/
622                                         jsonType = JsonType.FLOAT;
623                                         state = E1;
624                                         break;
625
626                                 case DF: /* floating point number detected by fraction */
627                                         jsonType = JsonType.FLOAT;
628                                         state = FX;
629                                         break;
630
631                                 case SB: /* string begin " or ' */
632                                         buffer.Length = 0;
633                                         quoteChar = ch;
634                                         jsonType = JsonType.STRING;
635                                         state = ST;
636                                         break;
637
638                                 case KB: /* unquoted key name begin */
639                                         jsonType = JsonType.STRING;
640                                         state = UK;
641                                         break;
642
643                                 case UE: /* unquoted key name end ':' */
644                                         RemoveLastCharFromBuffer ();
645                                         if (ParseBuffer (out result))
646                                                 StoreKey (result);
647                                         jsonType = JsonType.NONE;
648                                         
649                                         PopMode (JsonMode.KEY);
650                                         PushMode (JsonMode.OBJECT);
651                                         state = VA;
652                                         buffer.Length = 0;
653                                         break;
654                                         
655                                 case NU: /* n */
656                                         jsonType = JsonType.NULL;
657                                         state = N1;
658                                         break;
659
660                                 case FA: /* f */
661                                         jsonType = JsonType.FALSE;
662                                         state = F1;
663                                         break;
664
665                                 case TR: /* t */
666                                         jsonType = JsonType.TRUE;
667                                         state = T1;
668                                         break;
669
670                                 case EO: /* empty } */
671                                         result = PopObject (true);
672                                         if (result != null)
673                                                 StoreValue (result);
674                                         PopMode (JsonMode.KEY);
675                                         state = OK;
676                                         break;
677
678                                 case OE: /* } */
679                                         RemoveLastCharFromBuffer ();
680
681                                         if (ParseBuffer (out result))
682                                                 StoreValue (result);
683
684                                         result = PopObject (true);
685                                         if (result != null)
686                                                 StoreValue (result);
687
688                                         PopMode (JsonMode.OBJECT);
689
690                                         jsonType = JsonType.NONE;
691                                         state = OK;
692                                         break;
693
694                                 case AE: /* ] */
695                                         RemoveLastCharFromBuffer ();
696                                         if (ParseBuffer (out result))
697                                                 StoreValue (result);
698                                         PopMode (JsonMode.ARRAY);
699                                         result = PopObject (true);
700                                         if (result != null)
701                                                 StoreValue (result);
702                                         
703                                         jsonType = JsonType.NONE;
704                                         state = OK;
705                                         break;
706
707                                 case OS: /* { */
708                                         RemoveLastCharFromBuffer ();
709                                         CreateObject ();
710                                         PushMode (JsonMode.KEY);
711                                         
712                                         state = OB;
713                                         break;
714
715                                 case AB: /* [ */
716                                         RemoveLastCharFromBuffer ();
717                                         CreateArray ();
718                                         PushMode (JsonMode.ARRAY);
719
720                                         state = AR;
721                                         break;
722
723                                 case SE: /* string end " or ' */
724                                         if (ch == quoteChar) {
725                                                 RemoveLastCharFromBuffer ();
726
727                                                 switch (PeekMode ()) {
728                                                         case JsonMode.KEY:
729                                                                 if (ParseBuffer (out result))
730                                                                         StoreKey (result);
731
732                                                                 jsonType = JsonType.NONE;
733                                                                 state = CO;
734                                                                 buffer.Length = 0;
735                                                                 break;
736
737                                                         case JsonMode.ARRAY:
738                                                         case JsonMode.OBJECT:
739                                                                 if (ParseBuffer (out result))
740                                                                         StoreValue (result);
741                                                         
742                                                                 jsonType = JsonType.NONE;
743                                                                 state = OK;
744                                                                 break;
745
746                                                         case JsonMode.NONE: /* A stand-alone string */
747                                                                 jsonType = JsonType.STRING;
748                                                                 state = IV; /* the rest of input is invalid */
749                                                                 if (ParseBuffer (out result))
750                                                                         PushObject (result);
751                                                                 break;
752                                                         
753                                                         default:
754                                                                 throw new ArgumentException ("Syntax error: string in unexpected place.");
755                                                 }
756                                         }
757                                         break;
758
759                                 case CM: /* , */
760                                         RemoveLastCharFromBuffer ();
761
762                                         // With MS.AJAX, a comma resets the recursion depth
763                                         recursionDepth = 0;
764
765                                         bool doStore = ParseBuffer (out result);
766                                         switch (PeekMode ()) {
767                                                 case JsonMode.OBJECT:
768                                                         if (doStore)
769                                                                 StoreValue (result);
770                                                         PopMode (JsonMode.OBJECT);
771                                                         PushMode (JsonMode.KEY);
772                                                         jsonType = JsonType.NONE;
773                                                         state = KE;
774                                                         break;
775
776                                                 case JsonMode.ARRAY:
777                                                         jsonType = JsonType.NONE;
778                                                         state = VA;
779                                                         if (doStore)
780                                                                 StoreValue (result);
781                                                         break;
782
783                                                 default:
784                                                         throw new ArgumentException ("Syntax error: unexpected comma.");
785                                         }
786                                         
787                                         break;
788
789                                 case CA: /* : */
790                                         RemoveLastCharFromBuffer ();
791
792                                         // With MS.AJAX a colon increases recursion depth
793                                         if (++recursionDepth >= recursionLimit)
794                                                 throw new ArgumentException ("Recursion limit has been reached on parsing input.");
795                                         
796                                         PopMode (JsonMode.KEY);
797                                         PushMode (JsonMode.OBJECT);
798                                         state = VA;
799                                         break;
800
801                                 case IF: /* Infinity */
802                                 case NN: /* NaN */
803                                         jsonType = JsonType.FLOAT;
804                                         switch (PeekMode ()) {
805                                                 case JsonMode.ARRAY:
806                                                 case JsonMode.OBJECT:
807                                                         if (ParseBuffer (out result))
808                                                                 StoreValue (result);
809                                                         
810                                                         jsonType = JsonType.NONE;
811                                                         state = OK;
812                                                         break;
813
814                                                 case JsonMode.NONE: /* A stand-alone NaN/Infinity */
815                                                         jsonType = JsonType.FLOAT;
816                                                         state = IV; /* the rest of input is invalid */
817                                                         if (ParseBuffer (out result))
818                                                                 PushObject (result);
819                                                         break;
820                                                         
821                                                 default:
822                                                         throw new ArgumentException ("Syntax error: misplaced NaN/Infinity.");
823                                         }
824                                         buffer.Length = 0;
825                                         break;
826                                         
827                                 default:
828                                         throw new ArgumentException (GetModeMessage (PeekMode ()));
829                         }
830                         
831                         return true;
832                 }
833
834                 void CreateArray ()
835                 {
836                         var arr = new ArrayList ();
837                         PushObject (arr);
838                 }
839                 
840                 void CreateObject ()
841                 {
842                         var dict = new Dictionary <string, object> ();
843                         PushObject (dict);
844                 }
845
846                 void StoreKey (object o)
847                 {
848                         string key = o as string;
849                         if (key != null)
850                                 key = key.Trim ();
851                         
852                         if (String.IsNullOrEmpty (key))
853                                 throw new InvalidOperationException ("Internal error: key is null, empty or not a string.");
854                         
855                         currentKey.Push (key);
856                         Dictionary <string, object> dict = PeekObject () as Dictionary <string, object>;
857                         if (dict == null)
858                                 throw new InvalidOperationException ("Internal error: current object is not a dictionary.");
859
860                         /* MS AJAX.NET silently overwrites existing currentKey value */
861                         dict [key] = null;
862                 }
863
864                 void StoreValue (object o)
865                 {
866                         Dictionary <string, object> dict = PeekObject () as Dictionary <string, object>;
867                         if (dict == null) {
868                                 ArrayList arr = PeekObject () as ArrayList;
869                                 if (arr == null)
870                                         throw new InvalidOperationException ("Internal error: current object is not a dictionary or an array.");
871                                 arr.Add (o);
872                                 return;
873                         }
874
875                         string key;
876                         if (currentKey.Count == 0)
877                                 key = null;
878                         else
879                                 key = currentKey.Pop ();
880
881                         if (String.IsNullOrEmpty (key))
882                                 throw new InvalidOperationException ("Internal error: object is a dictionary, but no key present.");
883                         
884                         dict [key] = o;
885                 }
886         }
887 }