context: //
  // JsonDeserializer.cs
  //
  // Author:
  //   Marek Habersack <grendel@twistedcode.net>
  //
  // (C) 2008 Novell, Inc.  http://novell.com/
  // Copyright 2011, Xamarin, Inc (http://xamarin.com)
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  // Code is based on JSON_checker (http://www.json.org/JSON_checker/) and JSON_parser
  // (http://fara.cs.uni-potsdam.de/~jsg/json_parser/) C sources.
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Data;
  using System.Globalization;
  using System.IO;
  using System.Reflection;
  using System.Text;
  
  namespace System.Web.Script.Serialization
  {
  	internal sealed class JsonDeserializer
  	{
  		/* Universal error constant */
  		const int __ = -1;
  		const int UNIVERSAL_ERROR = __;
  		
  		/*
  		  Characters are mapped into these 31 character classes. This allows for
  		  a significant reduction in the size of the state transition table.
  		*/
  		const int C_SPACE = 0x00;  /* space */
  		const int C_WHITE = 0x01;  /* other whitespace */
  		const int C_LCURB = 0x02;  /* {  */
  		const int C_RCURB = 0x03;  /* } */
  		const int C_LSQRB = 0x04;  /* [ */
  		const int C_RSQRB = 0x05;  /* ] */
  		const int C_COLON = 0x06;  /* : */
  		const int C_COMMA = 0x07;  /* , */
  		const int C_QUOTE = 0x08;  /* " */
  		const int C_BACKS = 0x09;  /* \ */
  		const int C_SLASH = 0x0A;  /* / */
  		const int C_PLUS  = 0x0B;  /* + */
  		const int C_MINUS = 0x0C;  /* - */
  		const int C_POINT = 0x0D;  /* . */
  		const int C_ZERO  = 0x0E;  /* 0 */
  		const int C_DIGIT = 0x0F;  /* 123456789 */
  		const int C_LOW_A = 0x10;  /* a */
  		const int C_LOW_B = 0x11;  /* b */
  		const int C_LOW_C = 0x12;  /* c */
  		const int C_LOW_D = 0x13;  /* d */
  		const int C_LOW_E = 0x14;  /* e */
  		const int C_LOW_F = 0x15;  /* f */
  		const int C_LOW_L = 0x16;  /* l */
  		const int C_LOW_N = 0x17;  /* n */
  		const int C_LOW_R = 0x18;  /* r */
  		const int C_LOW_S = 0x19;  /* s */
  		const int C_LOW_T = 0x1A;  /* t */
  		const int C_LOW_U = 0x1B;  /* u */
  		const int C_ABCDF = 0x1C;  /* ABCDF */
  		const int C_E     = 0x1D;  /* E */
  		const int C_ETC   = 0x1E;  /* everything else */
  		const int C_STAR  = 0x1F;  /* * */
  		const int C_I     = 0x20;  /* I */
  		const int C_LOW_I = 0x21;  /* i */
  		const int C_LOW_Y = 0x22;  /* y */
  		const int C_N     = 0x23;  /* N */
  		
  		/* The state codes. */
  		const int GO = 0x00;  /* start    */
  		const int OK = 0x01;  /* ok       */
  		const int OB = 0x02;  /* object   */
  		const int KE = 0x03;  /* key      */
  		const int CO = 0x04;  /* colon    */
  		const int VA = 0x05;  /* value    */
  		const int AR = 0x06;  /* array    */
  		const int ST = 0x07;  /* string   */
  		const int ES = 0x08;  /* escape   */
  		const int U1 = 0x09;  /* u1       */
  		const int U2 = 0x0A;  /* u2       */
  		const int U3 = 0x0B;  /* u3       */
  		const int U4 = 0x0C;  /* u4       */
  		const int MI = 0x0D;  /* minus    */
  		const int ZE = 0x0E;  /* zero     */
  		const int IN = 0x0F;  /* integer  */
  		const int FR = 0x10;  /* fraction */
  		const int E1 = 0x11;  /* e        */
  		const int E2 = 0x12;  /* ex       */
  		const int E3 = 0x13;  /* exp      */
  		const int T1 = 0x14;  /* tr       */
  		const int T2 = 0x15;  /* tru      */
  		const int T3 = 0x16;  /* true     */
  		const int F1 = 0x17;  /* fa       */
  		const int F2 = 0x18;  /* fal      */
  		const int F3 = 0x19;  /* fals     */
  		const int F4 = 0x1A;  /* false    */
  		const int N1 = 0x1B;  /* nu       */
  		const int N2 = 0x1C;  /* nul      */
  		const int N3 = 0x1D;  /* null     */
  		const int FX = 0x1E;  /* *.* *eE* */
  		const int IV = 0x1F;  /* invalid input */
  		const int UK = 0x20;  /* unquoted key name */
  		const int UI = 0x21; /* ignore during unquoted key name construction */
  		const int I1 = 0x22;  /* In */
  		const int I2 = 0x23;  /* Inf */
  		const int I3 = 0x24;  /* Infi */
  		const int I4 = 0x25;  /* Infin */
  		const int I5 = 0x26;  /* Infini */
  		const int I6 = 0x27;  /* Infinit */
  		const int I7 = 0x28;  /* Infinity */
  		const int V1 = 0x29;  /* Na */
  		const int V2 = 0x2A;  /* NaN */
  		
  		/* Actions */
  		const int FA = -10; /* false */
  		const int TR = -11; /* false */
  		const int NU = -12; /* null */
  		const int DE = -13; /* double detected by exponent e E */
  		const int DF = -14; /* double detected by fraction . */
  		const int SB = -15; /* string begin */
  		const int MX = -16; /* integer detected by minus */
  		const int ZX = -17; /* integer detected by zero */
  		const int IX = -18; /* integer detected by 1-9 */
  		const int EX = -19; /* next char is escaped */
  		const int UC = -20; /* Unicode character read */
  		const int SE =  -4; /* string end */
  		const int AB =  -5; /* array begin */
  		const int AE =  -7; /* array end */
  		const int OS =  -6; /* object start */
  		const int OE =  -8; /* object end */
  		const int EO =  -9; /* empty object */
  		const int CM =  -3; /* comma */
  		const int CA =  -2; /* colon action */
  		const int PX = -21; /* integer detected by plus */
  		const int KB = -22; /* unquoted key name begin */
  		const int UE = -23; /* unquoted key name end */
  		const int IF = -25; /* Infinity */
  		const int NN = -26; /* NaN */
  		
  		
  		enum JsonMode {
  			NONE,
  			ARRAY,
  			DONE,
  			KEY,
  			OBJECT
  		};
  
  		enum JsonType {
  			NONE = 0,
  			ARRAY_BEGIN,
  			ARRAY_END,
  			OBJECT_BEGIN,
  			OBJECT_END,
  			INTEGER,
  			FLOAT,
  			NULL,
  			TRUE,
  			FALSE,
  			STRING,
  			KEY,
  			MAX
  		};
  		
  		/*
  		  This array maps the 128 ASCII characters into character classes.
  		  The remaining Unicode characters should be mapped to C_ETC.
  		  Non-whitespace control characters are errors.
  		*/
  		static readonly int[] ascii_class = {
  			__,      __,      __,      __,      __,      __,      __,      __,
  			__,      C_WHITE, C_WHITE, __,      __,      C_WHITE, __,      __,
  			__,      __,      __,      __,      __,      __,      __,      __,
  			__,      __,      __,      __,      __,      __,      __,      __,
  
  			C_SPACE, C_ETC,   C_QUOTE, C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_QUOTE,
  			C_ETC,   C_ETC,   C_STAR,   C_PLUS,  C_COMMA, C_MINUS, C_POINT, C_SLASH,
  			C_ZERO,  C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
  			C_DIGIT, C_DIGIT, C_COLON, C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
  
  			C_ETC,   C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E,     C_ABCDF, C_ETC,
  			C_ETC,   C_I,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_N,   C_ETC,
  			C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
  			C_ETC,   C_ETC,   C_ETC,   C_LSQRB, C_BACKS, C_RSQRB, C_ETC,   C_ETC,
  
  			C_ETC,   C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC,
  			C_ETC,   C_LOW_I, C_ETC,   C_ETC,   C_LOW_L, C_ETC,   C_LOW_N, C_ETC,
  			C_ETC,   C_ETC,   C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC,   C_ETC,
  			C_ETC,   C_LOW_Y,   C_ETC,   C_LCURB, C_ETC,   C_RCURB, C_ETC,   C_ETC
  		};
  
  		static readonly int[,] state_transition_table = {
  		/*
  		  The state transition table takes the current state and the current symbol,
  		  and returns either a new state or an action. An action is represented as a
  		  negative number. A JSON text is accepted if at the end of the text the
  		  state is OK and if the mode is MODE_DONE.
  
  		                           white                  '                   1-9                                   ABCDF  etc
  		                       space |  {  }  [  ]  :  ,  "  \  /  +  -  .  0  |  a  b  c  d  e  f  l  n  r  s  t  u  |  E  |  *  I  i  y  N */
  			/*start    GO*/ {GO,GO,OS,__,AB,__,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,__,__,__,TR,__,__,__,__,__,I1,__,__,V1},
  			/*ok       OK*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*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},
  			/*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},
  			/*colon    CO*/ {CO,CO,__,__,__,__,CA,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*value    VA*/ {VA,VA,OS,__,AB,__,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__,I1,__,__,V1},
  			/*array    AR*/ {AR,AR,OS,__,AB,AE,__,__,SB,__,__,PX,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__,I1,__,__,V1},
  			/*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},
  			/*escape   ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__,__,__,__,__,__},
  			/*u1       U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__,__,__,__,__,__},
  			/*u2       U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__,__,__,__,__,__},
  			/*u3       U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__,__,__,__,__,__},
  			/*u4       U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,UC,UC,UC,UC,UC,UC,UC,UC,__,__,__,__,__,__,UC,UC,__,__,__,__,__,__},
  			/*minus    MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IN,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I1,__,__,__},
  			/*zero     ZE*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,DF,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*int      IN*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,DF,IN,IN,__,__,__,__,DE,__,__,__,__,__,__,__,__,DE,__,__,__,__,__,__},
  			/*frac     FR*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__,__,__,__,__},
  			/*e        E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*ex       E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*exp      E3*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*tr       T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__,__,__,__,__,__},
  			/*tru      T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__,__,__,__,__,__},
  			/*true     T3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*fa       F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*fal      F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*fals     F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__,__,__,__,__,__},
  			/*false    F4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*nu       N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__,__,__,__,__,__},
  			/*nul      N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*null     N3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*_.       FX*/ {OK,OK,__,OE,__,AE,__,CM,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__,__,__,__,__},
  			/*inval.   IV*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*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},
  			/*unq.ign. UI*/ {UI,UI,__,__,__,__,UE,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*i1       I1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I2,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*i2       I2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I3,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*i3       I3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I4,__,__},
  			/*i4       I4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I5,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*i5       I5*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I6,__,__},
  			/*i6       I6*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,I7,__,__,__,__,__,__,__,__,__},
  			/*i7       I7*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,IF,__},
  			/*v1       V1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,V2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
  			/*v2       V2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,NN},
  		};
  
  		JavaScriptSerializer serializer;
  		JavaScriptTypeResolver typeResolver;
  		int maxJsonLength;
  		int currentPosition;
  		int recursionLimit;
  		int recursionDepth;
  		
  		Stack <JsonMode> modes;
  		Stack <object> returnValue;
  		JsonType jsonType;
  		
  		bool escaped;
  		int state;
  		Stack <string> currentKey;
  		StringBuilder buffer;
  		char quoteChar;
  		
  		public JsonDeserializer (JavaScriptSerializer serializer)
  		{
  			this.serializer = serializer;
  			this.maxJsonLength = serializer.MaxJsonLength;
  			this.recursionLimit = serializer.RecursionLimit;
  			this.typeResolver = serializer.TypeResolver;
  			this.modes = new Stack <JsonMode> ();
  			this.currentKey = new Stack <string> ();
  			this.returnValue = new Stack <object> ();
  			this.state = GO;
  			this.currentPosition = 0;
  			this.recursionDepth = 0;
  		}
  
  		public object Deserialize (string input)
  		{
  			if (input == null)
  				throw new ArgumentNullException ("input");
  
  			return Deserialize (new StringReader (input));
  		}
  		
  		public object Deserialize (TextReader input)
  		{
  			if (input == null)
  				throw new ArgumentNullException ("input");
  			
  			int value;
  			buffer = new StringBuilder ();
  			
  			while (true) {
  				value = input.Read ();
  				if (value < 0)
  					break;
  
  				currentPosition++;
  				if (currentPosition > maxJsonLength)
  					throw new ArgumentException ("Maximum JSON input length has been exceeded.");
  
  				if (!ProcessCharacter ((char) value))
  					throw new InvalidOperationException ("JSON syntax error.");
  			}
  
  			object topObject = PeekObject ();
  			if (buffer.Length > 0) {
  				object result;
  
  				if (ParseBuffer (out result)) {
  					if (topObject != null)
  						StoreValue (result);
  					else
  						PushObject (result);
  				}
  			}
  
  			if (returnValue.Count > 1)
  				throw new InvalidOperationException ("JSON syntax error.");
  
  			object ret = PopObject ();
  			return ret;
  		}
  
  #if DEBUG
  		void DumpObject (string indent, object obj)
  		{
  			if (obj is Dictionary <string, object>) {
  				Console.WriteLine (indent + "{");
  				foreach (KeyValuePair <string, object> kvp in (Dictionary <string, object>)obj) {
  					Console.WriteLine (indent + "\t\"{0}\": ", kvp.Key);
  					DumpObject (indent + "\t\t", kvp.Value);
  				}
  				Console.WriteLine (indent + "}");
  			} else if (obj is object[]) {
  				Console.WriteLine (indent + "[");
  				foreach (object o in (object[])obj)
  					DumpObject (indent + "\t", o);
  				Console.WriteLine (indent + "]");
  			} else if (obj != null)
  				Console.WriteLine (indent + obj.ToString ());
  			else
  				Console.WriteLine ("null");
  		}
  #endif
  		
  		void DecodeUnicodeChar ()
  		{
  			int len = buffer.Length;
  			if (len < 6)
  				throw new ArgumentException ("Invalid escaped unicode character specification (" + currentPosition + ")");
  
  			int code = Int32.Parse (buffer.ToString ().Substring (len - 4), NumberStyles.HexNumber);
  			buffer.Length = len - 6;
  			buffer.Append ((char)code);
  		}
  
  		string GetModeMessage (JsonMode expectedMode)
  		{
  			switch (expectedMode) {
  				case JsonMode.ARRAY:
  					return "Invalid array passed in, ',' or ']' expected (" + currentPosition + ")";
  
  				case JsonMode.KEY:
  					return "Invalid object passed in, key name or ':' expected (" + currentPosition + ")";
  
  				case JsonMode.OBJECT:
  					return "Invalid object passed in, key value expected (" + currentPosition + ")";
  					
  				default:
  					return "Invalid JSON string";
  			}
  		}
  		
  		void PopMode (JsonMode expectedMode)
  		{
  			JsonMode mode = PeekMode ();
  			if (mode != expectedMode)
  				throw new ArgumentException (GetModeMessage (mode));
  
  			modes.Pop ();
  		}
  
  		void PushMode (JsonMode newMode)
  		{
  			modes.Push (newMode);
  		}
  
  		JsonMode PeekMode ()
  		{
  			if (modes.Count == 0)
  				return JsonMode.NONE;
  
  			return modes.Peek ();
  		}
  
  		void PushObject (object o)
  		{
  			returnValue.Push (o);
  		}
  
  		object PopObject (bool notIfLast)
  		{
  			int count = returnValue.Count;
  			if (count == 0)
  				return null;
  
  			if (notIfLast && count == 1)
  				return null;
  			
  			return returnValue.Pop ();
  		}
  
  		object PopObject ()
  		{
  			return PopObject (false);
  		}
  		
  		object PeekObject ()
  		{
  			if (returnValue.Count == 0)
  				return null;
  
  			return returnValue.Peek ();
  		}
  		
  		void RemoveLastCharFromBuffer ()
  		{
  			int len = buffer.Length;
  			if (len == 0)
  				return;
  			buffer.Length = len - 1;
  		}
  
  		bool ParseBuffer (out object result)
  		{
  			result = null;
  			
  			if (jsonType == JsonType.NONE) {
  				buffer.Length = 0;
  				return false;
  			}
  
  			string s = buffer.ToString ();
  			bool converted = true;
  			int intValue;
  			long longValue;
  			decimal decimalValue;
  			double doubleValue;
  			
  			switch (jsonType) {
  				case JsonType.INTEGER:
  					/* MS AJAX.NET JSON parser promotes big integers to double */
  					
  					if (Int32.TryParse (s, out intValue))
  						result = intValue;
  					else if (Int64.TryParse (s, out longValue))
  						result = longValue;
  					else if (Decimal.TryParse (s, out decimalValue))
  						result = decimalValue;
  					else if (Double.TryParse (s, out doubleValue))
  						result = doubleValue;
  					else
  						converted = false;
  					break;
  
  				case JsonType.FLOAT:
  					if (Decimal.TryParse (s, out decimalValue))
  						result = decimalValue;
  					else if (Double.TryParse (s, out doubleValue))
  						result = doubleValue;
  					else
  						converted = false;
  					break;
  
  				case JsonType.TRUE:
  					if (String.Compare (s, "true", StringComparison.Ordinal) == 0)
  						result = true;
  					else
  						converted = false;
  					break;
  
  				case JsonType.FALSE:
  					if (String.Compare (s, "false", StringComparison.Ordinal) == 0)
  						result = false;
  					else
  						converted = false;
  					break;
  
  				case JsonType.NULL:
  					if (String.Compare (s, "null", StringComparison.Ordinal) != 0)
  						converted = false;
  					break;
  
  				case JsonType.STRING:
  					if (s.StartsWith ("/Date(", StringComparison.Ordinal) && s.EndsWith (")/", StringComparison.Ordinal)) {
  						long javaScriptTicks = Convert.ToInt64 (s.Substring (6, s.Length - 8));
  						result = new DateTime ((javaScriptTicks * 10000) + JsonSerializer.InitialJavaScriptDateTicks, DateTimeKind.Utc);
  					} else
  						result = s;
  					break;
  
  				default:
  					throw new InvalidOperationException (String.Format ("Internal error: unexpected JsonType ({0})", jsonType));
  					
  			}
  
  			if (!converted)
  				throw new ArgumentException ("Invalid JSON primitive: " + s);
  
  			buffer.Length = 0;
  			return true;
  		}
  		
  		bool ProcessCharacter (char ch)
  		{
  			int next_class, next_state;
  
  			if (ch >= 128)
  				next_class = C_ETC;
  			else {
  				next_class = ascii_class [ch];
  				if (next_class <= UNIVERSAL_ERROR)
  					return false;
  			}
  
  			if (escaped) {
  				escaped = false;
  				RemoveLastCharFromBuffer ();
  				
  				switch (ch) {
  					case 'b':
  						buffer.Append ('\b');
  						break;
  					case 'f':
  						buffer.Append ('\f');
  						break;
  					case 'n':
  						buffer.Append ('\n');
  						break;
  					case 'r':
  						buffer.Append ('\r');
  						break;
  					case 't':
  						buffer.Append ('\t');
  						break;
  					case '"':
  					case '\\':
  					case '/':
  						buffer.Append (ch);
  						break;
  					case 'u':
  						buffer.Append ("\\u");
  						break;
  					default:
  						return false;
  				}
  			} else if (jsonType != JsonType.NONE || !(next_class == C_SPACE || next_class == C_WHITE))
  				buffer.Append (ch);
  
  			next_state = state_transition_table [state, next_class];
  			if (next_state >= 0) {
  				state = next_state;
  				return true;
  			}
  
  			object result;
  			/* An action to perform */
  			switch (next_state) {
  				case UC: /* Unicode character */
  					DecodeUnicodeChar ();
  					state = ST;
  					break;
  					
  				case EX: /* Escaped character */
  					escaped = true;
  					state = ES;
  					break;
  
  				case MX: /* integer detected by minus */
  					jsonType = JsonType.INTEGER;
  					state = MI;
  					break;
  
  				case PX: /* integer detected by plus */
  					jsonType = JsonType.INTEGER;
  					state = MI;
  					break;
  					
  				case ZX: /* integer detected by zero */
  					jsonType = JsonType.INTEGER;
  					state = ZE;
  					break;
  
  				case IX: /* integer detected by 1-9 */
  					jsonType = JsonType.INTEGER;
  					state = IN;
  					break;
  
  				case DE: /* floating point number detected by exponent*/
  					jsonType = JsonType.FLOAT;
  					state = E1;
  					break;
  
  				case DF: /* floating point number detected by fraction */
  					jsonType = JsonType.FLOAT;
  					state = FX;
  					break;
  
  				case SB: /* string begin " or ' */
  					buffer.Length = 0;
  					quoteChar = ch;
  					jsonType = JsonType.STRING;
  					state = ST;
  					break;
  
  				case KB: /* unquoted key name begin */
  					jsonType = JsonType.STRING;
  					state = UK;
  					break;
  
  				case UE: /* unquoted key name end ':' */
  					RemoveLastCharFromBuffer ();
  					if (ParseBuffer (out result))
  						StoreKey (result);
  					jsonType = JsonType.NONE;
  					
  					PopMode (JsonMode.KEY);
  					PushMode (JsonMode.OBJECT);
  					state = VA;
  					buffer.Length = 0;
  					break;
  					
  				case NU: /* n */
  					jsonType = JsonType.NULL;
  					state = N1;
  					break;
  
  				case FA: /* f */
  					jsonType = JsonType.FALSE;
  					state = F1;
  					break;
  
  				case TR: /* t */
  					jsonType = JsonType.TRUE;
  					state = T1;
  					break;
  
  				case EO: /* empty } */
  					result = PopObject (true);
  					if (result != null)
  						StoreValue (result);
  					PopMode (JsonMode.KEY);
  					state = OK;
  					break;
  
  				case OE: /* } */
  					RemoveLastCharFromBuffer ();
  
  					if (ParseBuffer (out result))
  						StoreValue (result);
  
  					result = PopObject (true);
  
gt: 					if (result != null)