Browse Source

Merge pull request #2 from fredericaltorres/master

JSON Changes 2
Sébastien Ros 11 years ago
parent
commit
89ec21a834

+ 2 - 2
Jint/Native/Json/JsonInstance.cs

@@ -39,7 +39,7 @@ namespace Jint.Native.Json
         {
         {
             var parser = new JsonParser(_engine);
             var parser = new JsonParser(_engine);
 
 
-            return parser.Parse(arguments[0].ToString());
+            return parser.Parse(arguments[0].AsString());
         }
         }
 
 
         public JsValue Stringify(JsValue thisObject, JsValue[] arguments)
         public JsValue Stringify(JsValue thisObject, JsValue[] arguments)
@@ -65,7 +65,7 @@ namespace Jint.Native.Json
             }
             }
 
 
             var serializer = new JsonSerializer(_engine);
             var serializer = new JsonSerializer(_engine);
-            if (value == Undefined.Instance) {
+            if (value == Undefined.Instance && replacer == Undefined.Instance) {
                 return Undefined.Instance;
                 return Undefined.Instance;
             }
             }
             else {
             else {

+ 133 - 13
Jint/Native/Json/JsonParser.cs

@@ -76,7 +76,7 @@ namespace Jint.Native.Json
                 }
                 }
                 else
                 else
                 {
                 {
-                    return char.MinValue;
+                    throw new JavaScriptException(_engine.SyntaxError, string.Format("Expected hexadecimal digit:{0}", _source));
                 }
                 }
             }
             }
             return (char)code;
             return (char)code;
@@ -130,8 +130,7 @@ namespace Jint.Native.Json
                             Range = new[] {start, _index}
                             Range = new[] {start, _index}
                         };
                         };
             }
             }
-
-            throw new Exception(Messages.UnexpectedToken);
+            throw new JavaScriptException(_engine.SyntaxError, string.Format(Messages.UnexpectedToken, code));
         }
         }
 
 
         private Token ScanNumericLiteral()
         private Token ScanNumericLiteral()
@@ -205,6 +204,74 @@ namespace Jint.Native.Json
                 };
                 };
         }
         }
 
 
+        const string TrueValue  = "true";
+        const string FalseValue = "false";
+        
+        private static bool IsNullChar(char ch)
+        {
+            return Null.Text.Contains(ch.ToString());
+        }
+
+        private static bool IsTrueOrFalseChar(char ch)
+        {
+            // TODO: Should be optimized
+            return TrueValue.Contains(ch.ToString()) || FalseValue.Contains(ch.ToString());
+        }
+
+        private Token ScanBooleanLiteral()
+        {
+            int start   = _index;
+            string s    = string.Empty;
+            
+            while (IsTrueOrFalseChar(_source.CharCodeAt(_index)))
+            {
+                s += _source.CharCodeAt(_index++).ToString();
+            }
+            
+            if (s == TrueValue || s == FalseValue)
+            {
+                return new Token
+                {
+                    Type = Tokens.BooleanLiteral,
+                    Value = s == TrueValue,
+                    LineNumber = _lineNumber,
+                    LineStart = _lineStart,
+                    Range = new[] { start, _index }
+                };
+            }
+            else 
+            {
+                throw new JavaScriptException(_engine.SyntaxError, string.Format(Messages.UnexpectedToken, s));
+            }
+        }
+
+        private Token ScanNullLiteral()
+        {
+            int start = _index;
+            string s = string.Empty;
+            
+            while (IsNullChar(_source.CharCodeAt(_index)))
+            {
+                s += _source.CharCodeAt(_index++).ToString();
+            }
+
+            if (s == Null.Text)
+            {
+                return new Token
+                {
+                    Type = Tokens.NullLiteral,
+                    Value = Null.Instance,
+                    LineNumber = _lineNumber,
+                    LineStart = _lineStart,
+                    Range = new[] { start, _index }
+                };
+            }
+            else
+            {
+                throw new JavaScriptException(_engine.SyntaxError, string.Format(Messages.UnexpectedToken, s));
+            }
+        }
+
         private Token ScanStringLiteral()
         private Token ScanStringLiteral()
         {
         {
             string str = "";
             string str = "";
@@ -223,10 +290,16 @@ namespace Jint.Native.Json
                     quote = char.MinValue;
                     quote = char.MinValue;
                     break;
                     break;
                 }
                 }
+
+                if ((int)ch <= 31)
+                {
+                    throw new JavaScriptException(_engine.SyntaxError, string.Format("Invalid character '{0}', position:{1}, string:{2}", ch, _index, _source));
+                }
                 
                 
                 if (ch == '\\')
                 if (ch == '\\')
                 {
                 {
                     ch = _source.CharCodeAt(_index++);
                     ch = _source.CharCodeAt(_index++);
+
                     if (ch > 0 || !IsLineTerminator(ch))
                     if (ch > 0 || !IsLineTerminator(ch))
                     {
                     {
                         switch (ch)
                         switch (ch)
@@ -312,9 +385,9 @@ namespace Jint.Native.Json
 
 
             if (quote != 0)
             if (quote != 0)
             {
             {
-                throw new Exception(Messages.UnexpectedToken);
+                throw new JavaScriptException(_engine.SyntaxError, string.Format(Messages.UnexpectedToken, _source));
             }
             }
-
+            
             return new Token
             return new Token
                 {
                 {
                     Type = Tokens.String,
                     Type = Tokens.String,
@@ -348,8 +421,9 @@ namespace Jint.Native.Json
                 return ScanPunctuator();
                 return ScanPunctuator();
             }
             }
 
 
-            // String literal starts with single quote (#39) or double quote (#34).
-            if (ch == 39 || ch == 34)
+            // String literal starts with double quote (#34).
+            // Single quote (#39) are not allowed in JSON.
+            if (ch == 34) 
             {
             {
                 return ScanStringLiteral();
                 return ScanStringLiteral();
             }
             }
@@ -370,6 +444,16 @@ namespace Jint.Native.Json
                 return ScanNumericLiteral();
                 return ScanNumericLiteral();
             }
             }
 
 
+            if (ch == TrueValue[0] || ch == FalseValue[0])
+            {
+                return ScanBooleanLiteral();
+            }
+
+            if (ch == Null.Text[0])
+            {
+                return ScanNullLiteral();
+            }
+
             return ScanPunctuator();
             return ScanPunctuator();
         }
         }
 
 
@@ -610,7 +694,6 @@ namespace Jint.Native.Json
 
 
         public ObjectInstance ParseJsonObject()
         public ObjectInstance ParseJsonObject()
         {
         {
-
             Expect("{");
             Expect("{");
 
 
             var obj = _engine.Object.Construct(Arguments.Empty);
             var obj = _engine.Object.Construct(Arguments.Empty);
@@ -624,6 +707,10 @@ namespace Jint.Native.Json
                 }
                 }
 
 
                 var name = Lex().Value.ToString();
                 var name = Lex().Value.ToString();
+                if (PropertyNameContainsInvalidChar0To31(name))
+                {
+                    throw new JavaScriptException(_engine.SyntaxError, string.Format("Invalid character in property name '{0}'", name));
+                }
 
 
                 Expect(":");
                 Expect(":");
                 var value = ParseJsonValue();
                 var value = ParseJsonValue();
@@ -641,6 +728,26 @@ namespace Jint.Native.Json
             return obj;
             return obj;
         }
         }
 
 
+        private bool PropertyNameContainsInvalidChar0To31(string s)
+        {    
+            const int max = 31;
+
+            for (var i = 0; i < s.Length; i++)
+            {
+                var val = (int)s[i];
+                if (val <= max)
+                    return true;
+            }
+            return false;
+        }
+        
+        /// <summary>
+        /// Optimization.
+        /// By calling Lex().Value for each type, we parse the token twice.
+        /// It was already parsed by the peek() method.
+        /// _lookahead.Value already contain the value.
+        /// </summary>
+        /// <returns></returns>
         private JsValue ParseJsonValue()
         private JsValue ParseJsonValue()
         {
         {
             Tokens type = _lookahead.Type;
             Tokens type = _lookahead.Type;
@@ -648,22 +755,26 @@ namespace Jint.Native.Json
 
 
             if (type == Tokens.NullLiteral)
             if (type == Tokens.NullLiteral)
             {
             {
+                var v = Lex().Value;
                 return Null.Instance;
                 return Null.Instance;
+                // if FromObject() is modified to handle JsValue of type null
+                // as input parameter we could use the following
+                // return JsValue.FromObject(Lex().Value);
             }
             }
             
             
             if (type == Tokens.String)
             if (type == Tokens.String)
             {
             {
-                return JsValue.FromObject(Lex().Value);
+                return JsValue.FromObject(Lex().Value.ToString());
             }
             }
-            
+
             if (type == Tokens.Number)
             if (type == Tokens.Number)
             {
             {
                 return JsValue.FromObject(Lex().Value);
                 return JsValue.FromObject(Lex().Value);
             }
             }
-            
+
             if (type == Tokens.BooleanLiteral)
             if (type == Tokens.BooleanLiteral)
             {
             {
-                return "true".Equals(Lex().Value);
+                return (bool)Lex().Value;
             }
             }
             
             
             if (Match("["))
             if (Match("["))
@@ -731,7 +842,16 @@ namespace Jint.Native.Json
             {
             {
                 MarkStart();
                 MarkStart();
                 Peek();
                 Peek();
-                return ParseJsonValue();
+                JsValue jsv = ParseJsonValue();
+
+                Peek();
+                Tokens type = _lookahead.Type;
+                object value = _lookahead.Value;                
+                if(_lookahead.Type != Tokens.EOF)
+                {
+                    throw new JavaScriptException(_engine.SyntaxError, string.Format("Unexpected {0} {1}", _lookahead.Type, _lookahead.Value));
+                }
+                return jsv;
             }
             }
             finally
             finally
             {
             {

+ 15 - 2
Jint/Native/Json/JsonSerializer.cs

@@ -27,6 +27,13 @@ namespace Jint.Native.Json
         {
         {
             _stack = new Stack<object>();
             _stack = new Stack<object>();
 
 
+            // for JSON.stringify(), any function passed as the first argument will return undefined
+            // if the replacer is not defined. The function is not called either.
+            if (value.Is<ICallable>() && replacer == Undefined.Instance) 
+            {
+                return Undefined.Instance;
+            }
+
             if (replacer.IsObject())
             if (replacer.IsObject())
             {
             {
                 if (replacer.Is<ICallable>())
                 if (replacer.Is<ICallable>())
@@ -87,7 +94,13 @@ namespace Jint.Native.Json
             // defining the gap
             // defining the gap
             if (space.IsNumber())
             if (space.IsNumber())
             {
             {
-                _gap = new System.String(' ', (int) System.Math.Min(10, space.AsNumber()));
+                if (space.AsNumber() > 0) {
+                    _gap = new System.String(' ', (int)System.Math.Min(10, space.AsNumber()));
+                }
+                else 
+                {
+                    _gap = string.Empty;
+                }
             }
             }
             else if (space.IsString())
             else if (space.IsString())
             {
             {
@@ -96,7 +109,7 @@ namespace Jint.Native.Json
             }
             }
             else
             else
             {
             {
-                _gap = "";
+                _gap = string.Empty;
             }
             }
 
 
             var wrapper = _engine.Object.Construct(Arguments.Empty);
             var wrapper = _engine.Object.Construct(Arguments.Empty);

+ 14 - 1
Jint/Runtime/JavaScriptException.cs

@@ -20,9 +20,22 @@ namespace Jint.Runtime
         }
         }
 
 
         public JavaScriptException(JsValue error)
         public JavaScriptException(JsValue error)
-            : base("")
+            : base(GetErrorMessage(error))
         {
         {
             _errorObject = error;
             _errorObject = error;
+            
+        }
+
+        private static string GetErrorMessage(JsValue error) 
+        {
+            if (error.IsObject())
+            {
+                var oi = error.AsObject();
+                var message = oi.Get("message").AsString();
+                return message;
+            }
+            else
+                return string.Empty;            
         }
         }
 
 
         public JsValue Error { get { return _errorObject; } }
         public JsValue Error { get { return _errorObject; } }