소스 검색

Implementing Number.prototype.toFixed

Sebastien Ros 11 년 전
부모
커밋
04b62b5614
4개의 변경된 파일208개의 추가작업 그리고 143개의 파일을 삭제
  1. 185 8
      Jint/Native/Number/NumberPrototype.cs
  2. 4 3
      Jint/Runtime/Arguments.cs
  3. 15 15
      Jint/Runtime/ExpressionIntepreter.cs
  4. 4 117
      Jint/Runtime/TypeConverter.cs

+ 185 - 8
Jint/Native/Number/NumberPrototype.cs

@@ -1,4 +1,5 @@
-using Jint.Runtime;
+using System;
+using Jint.Runtime;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Number
@@ -27,9 +28,9 @@ namespace Jint.Native.Number
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance<object, object>(Engine, ToNumberString), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance<object, string>(Engine, ToNumberString), true, false, true);
             FastAddProperty("toLocaleString", new ClrFunctionInstance<object, object>(Engine, ToLocaleString), true, false, true);
-            FastAddProperty("valueOf", new ClrFunctionInstance<object, object>(Engine, ValueOf), true, false, true);
+            FastAddProperty("valueOf", new ClrFunctionInstance<object, double>(Engine, ValueOf), true, false, true);
             FastAddProperty("toFixed", new ClrFunctionInstance<object, object>(Engine, ToFixed), true, false, true);
             FastAddProperty("toExponential", new ClrFunctionInstance<object, object>(Engine, ToExponential), true, false, true);
             FastAddProperty("toPrecision", new ClrFunctionInstance<object, object>(Engine, ToPrecision), true, false, true);
@@ -40,7 +41,7 @@ namespace Jint.Native.Number
             throw new System.NotImplementedException();
         }
 
-        private object ValueOf(object thisObj, object[] arguments)
+        private double ValueOf(object thisObj, object[] arguments)
         {
             var number = thisObj as NumberInstance;
             if (number == null)
@@ -53,7 +54,64 @@ namespace Jint.Native.Number
 
         private object ToFixed(object thisObj, object[] arguments)
         {
-            throw new System.NotImplementedException();
+            var f = (int) TypeConverter.ToInteger(arguments.At(0, 0));
+            if (f < 0 || f > 20)
+            {
+                throw new JavaScriptException(Engine.RangeError, "fractionDigits argument must be between 0 and 20");
+            }
+
+            var x = TypeConverter.ToNumber(thisObj);
+
+            if (double.IsNaN(x))
+            {
+                return "NaN";
+            }
+
+            var sign = "";
+            if (x < 0)
+            {
+                sign = "-";
+                x = -x;
+            }
+
+            string m = "";
+            if (x >= System.Math.Pow(10, 21))
+            {
+                m = TypeConverter.ToString(x);
+            }
+            else
+            {
+                var d = (Decimal)x;
+                var significants = GetSignificantDigitCount(d);
+                var s = significants.Item1;
+                var kdigits = (int)System.Math.Floor(System.Math.Log10((double)s) + 1);
+                var n = kdigits - significants.Item2;
+
+                if (n == 0)
+                {
+                    m = "0";
+                }
+                else
+                {
+                    m = (System.Math.Round(x, f) * System.Math.Pow(10,f)).ToString();
+                }
+
+                if (f != 0)
+                {
+                    var k = m.Length;
+                    if (k <= f)
+                    {
+                        var z = new System.String('0', f + 1 - k);
+                        m += z;
+                        k = f + 1;
+                    }
+                    var a = m.Substring(0, k - f);
+                    var b = m.Substring(k - f);
+                    m = a + "." + b;
+                }
+            }
+
+            return sign + m;
         }
 
         private object ToExponential(object thisObj, object[] arguments)
@@ -66,14 +124,133 @@ namespace Jint.Native.Number
             throw new System.NotImplementedException();
         }
 
-        private object ToNumberString(object thisObject, object[] arguments)
+        private string ToNumberString(object thisObject, object[] arguments)
         {
-            if (TypeConverter.GetType(thisObject) != Types.Number)
+            if (TypeConverter.GetType(thisObject) != Types.Number && !(thisObject is NumberInstance))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            return TypeConverter.ToString(thisObject);
+            return ToNumberString(TypeConverter.ToNumber(thisObject));
+        }
+
+        public static string ToNumberString(double m) 
+        {
+            if (double.IsNaN(m))
+            {
+                return "NaN";
+            }
+
+            if (m == 0)
+            {
+                return "0";
+            }
+
+            if (m < 0)
+            {
+                return "-" + ToNumberString(-m);
+            }
+
+            if (double.IsPositiveInfinity(m) || m >= double.MaxValue)
+            {
+                return "Infinity";
+            }
+
+            if (double.IsNegativeInfinity(m) || m <= -double.MaxValue)
+            {
+                return "-Infinity";
+            }
+
+            // s is all digits (significand or mantissa)
+            // k number of digits of s
+            // n total of digits in fraction s*10^n-k=m
+            // 123.4 s=1234, k=4, n=3
+            // 1234000 s = 1234, k=4, n=7
+            var d = (Decimal)m;
+            var significants = GetSignificantDigitCount(d);
+            var s = significants.Item1;
+            var k = (int)System.Math.Floor(System.Math.Log10((double)s) + 1);
+            var n = k - significants.Item2;
+            if (m < 1 && m > -1)
+            {
+                n++;
+            }
+
+            while (s % 10 == 0)
+            {
+                s = s / 10;
+                k--;
+            }
+
+
+            if (k <= n && n <= 21)
+            {
+                return s + new string('0', n - k);
+            }
+
+            if (0 < n && n <= 21)
+            {
+                return s.ToString().Substring(0, n) + '.' + s.ToString().Substring(n);
+            }
+
+            if (-6 < n && n <= 0)
+            {
+                return "0." + new string('0', -n) + s;
+            }
+
+            if (k == 1)
+            {
+                return s + "e" + (n - 1 < 0 ? "-" : "+") + System.Math.Abs(n - 1);
+            }
+
+            return s.ToString().Substring(0, 1) + "." + s.ToString().Substring(1) + "e" + (n - 1 < 0 ? "-" : "+") + System.Math.Abs(n - 1);
+        }
+
+        public static Tuple<decimal, int> GetSignificantDigitCount(decimal value)
+        {
+            /* So, the decimal type is basically represented as a fraction of two
+             * integers: a numerator that can be anything, and a denominator that is 
+             * some power of 10.
+             * 
+             * For example, the following numbers are represented by
+             * the corresponding fractions:
+             * 
+             * VALUE    NUMERATOR   DENOMINATOR
+             * 1        1           1
+             * 1.0      10          10
+             * 1.012    1012        1000
+             * 0.04     4           100
+             * 12.01    1201        100
+             * 
+             * So basically, if the magnitude is greater than or equal to one,
+             * the number of digits is the number of digits in the numerator.
+             * If it's less than one, the number of digits is the number of     digits
+             * in the denominator.
+             */
+
+            int[] bits = decimal.GetBits(value);
+            int scalePart = bits[3];
+            int highPart = bits[2];
+            int middlePart = bits[1];
+            int lowPart = bits[0];
+
+            if (value >= 1M || value <= -1M)
+            {
+
+                var num = new decimal(lowPart, middlePart, highPart, false, 0);
+
+                return new Tuple<decimal, int>(num, (scalePart >> 16) & 0x7fff);
+            }
+            else
+            {
+
+                // Accoring to MSDN, the exponent is represented by
+                // bits 16-23 (the 2nd word):
+                // http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx
+                int exponent = (scalePart & 0x00FF0000) >> 16;
+
+                return new Tuple<decimal, int>(lowPart, exponent + 1);
+            }
         }
 
     }

+ 4 - 3
Jint/Runtime/Arguments.cs

@@ -15,11 +15,12 @@ namespace Jint.Runtime
         /// Returns the arguments at the provided position or Undefined if not present
         /// </summary>
         /// <param name="args"></param>
-        /// <param name="index"></param>
+        /// <param name="index">The index of the parameter to return</param>
+        /// <param name="undefinedValue">The value to return is the parameter is not provided</param>
         /// <returns></returns>
-        public static object At(this object[] args, int index)
+        public static object At(this object[] args, int index, object undefinedValue = null)
         {
-            return args.Length > index ? args[index] : Undefined.Instance;
+            return args.Length > index ? args[index] : undefinedValue ?? Undefined.Instance;
         }
     }
 }

+ 15 - 15
Jint/Runtime/ExpressionIntepreter.cs

@@ -166,7 +166,7 @@ namespace Jint.Runtime
                     return double.NaN;
                 }
 
-                if (double.IsInfinity(lN) && rN == 0)
+                if (double.IsInfinity(lN) && rN.Equals(0))
                 {
                     if (NumberInstance.IsNegativeZero(rN))
                     {
@@ -176,12 +176,12 @@ namespace Jint.Runtime
                     return lN;
                 }
 
-                if (lN == 0 && rN == 0)
+                if (lN.Equals(0) && rN.Equals(0))
                 {
                     return double.NaN;
                 }
 
-                if (rN == 0)
+                if (rN.Equals(0))
                 {
                     if (NumberInstance.IsNegativeZero(rN))
                     {
@@ -264,7 +264,7 @@ namespace Jint.Runtime
 
                 case ">=":
                     value = Compare(left, right);
-                    if (value == Undefined.Instance || (bool) value == true)
+                    if (value == Undefined.Instance || (bool) value)
                     {
                         value = false;
                     }
@@ -284,7 +284,7 @@ namespace Jint.Runtime
                 
                 case "<=":
                     value = Compare(right, left, false);
-                    if (value == Undefined.Instance || (bool) value == true)
+                    if (value == Undefined.Instance || (bool) value)
                     {
                         value = false;
                     }
@@ -397,7 +397,7 @@ namespace Jint.Runtime
                         return false;
                     }
 
-                    if (nx == ny)
+                    if (nx.Equals(ny))
                     {
                         return true;
                     }
@@ -489,7 +489,7 @@ namespace Jint.Runtime
                     return false;
                 }
 
-                if (nx == ny)
+                if (nx.Equals(ny))
                 {
                     return true;
                 }
@@ -530,9 +530,9 @@ namespace Jint.Runtime
                     return true;
                 }
 
-                if (nx == ny)
+                if (nx.Equals(ny))
                 {
-                    if (nx == 0)
+                    if (nx.Equals(0))
                     {
                         // +0 !== -0
                         return NumberInstance.IsNegativeZero(nx) == NumberInstance.IsNegativeZero(ny);
@@ -581,27 +581,27 @@ namespace Jint.Runtime
                     return Undefined.Instance;
                 }
 
-                if (nx == ny)
+                if (nx.Equals(ny))
                 {
                     return false;
                 }
 
-                if (nx == double.PositiveInfinity)
+                if (double.IsPositiveInfinity(nx))
                 {
                     return false;
                 }
 
-                if (ny == double.PositiveInfinity)
+                if (double.IsPositiveInfinity(ny))
                 {
                     return true;
                 }
 
-                if (ny == double.NegativeInfinity)
+                if (double.IsNegativeInfinity(ny))
                 {
                     return false;
                 }
 
-                if (nx == double.NegativeInfinity)
+                if (double.IsNegativeInfinity(nx))
                 {
                     return true;
                 }
@@ -623,7 +623,7 @@ namespace Jint.Runtime
         {
             if (literal.Type == SyntaxNodes.RegularExpressionLiteral)
             {
-                return _engine.RegExp.Construct((string) literal.Raw);
+                return _engine.RegExp.Construct(literal.Raw);
             }
 
             return literal.Value ?? Null.Instance;

+ 4 - 117
Jint/Runtime/TypeConverter.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Globalization;
 using Jint.Native;
+using Jint.Native.Number;
 using Jint.Native.Object;
 
 namespace Jint.Runtime
@@ -301,12 +302,12 @@ namespace Jint.Runtime
 
             if (o == Undefined.Instance)
             {
-                return "undefined";
+                return Undefined.Instance.ToString();
             }
 
             if (o == Null.Instance)
             {
-                return "null";
+                return Null.Instance.ToString();
             }
 
             var p = o as IPrimitiveType;
@@ -323,75 +324,7 @@ namespace Jint.Runtime
             if (o is double)
             {
                 var m = (double) o;
-                if (double.IsNaN(m))
-                {
-                    return "NaN";
-                }
-
-                if (m == 0)
-                {
-                    return "0";
-                }
-
-                if (m < 0)
-                {
-                    return "-" + ToString(-m);
-                }
-
-                if (m == double.PositiveInfinity || m >= double.MaxValue)
-                {
-                    return "Infinity";
-                }
-
-                if (m == double.NegativeInfinity || m <= -double.MaxValue)
-                {
-                    return "-Infinity";
-                }
-
-                // s is all digits (significand or mantissa)
-                // k number of digits of s
-                // n total of digits in fraction s*10^n-k=m
-                // 123.4 s=1234, k=4, n=3
-                // 1234000 s = 1234, k=4, n=7
-                var d = (Decimal) m;
-                var significants = GetSignificantDigitCount(d);
-                var s = significants.Item1;
-                var k = (int)Math.Floor(Math.Log10((double) s)+1);
-                var n = k - significants.Item2;
-                if (m < 1 && m > -1)
-                {
-                    n++;
-                }
-
-                while (s%10 == 0)
-                {
-                    s = s/10;
-                    k--;
-                }
-
-                
-                if (k <= n && n <= 21)
-                {
-                    return s + new string('0', (int)(n - k));
-                }
-
-                if (0 < n && n <= 21)
-                {
-                    return s.ToString().Substring(0, (int)n) + '.' + s.ToString().Substring((int)n);
-                }
-
-                if (-6 < n && n <= 0)
-                {
-                    return "0." + new string('0', -n) + s;
-                }
-
-                if (k == 1)
-                {
-                    return s + "e" + (n - 1 < 0 ? "-" : "+") + Math.Abs(n - 1);
-                }
-
-                return s.ToString().Substring(0, 1) + "." + s.ToString().Substring(1) + "e" + (n - 1 < 0 ? "-" : "+") +
-                       Math.Abs(n - 1);
+                return NumberPrototype.ToNumberString(m);
             }
 
             if (o is int)
@@ -502,51 +435,5 @@ namespace Jint.Runtime
             }
         }
 
-        public static Tuple<decimal, int> GetSignificantDigitCount(decimal value)
-        {
-            /* So, the decimal type is basically represented as a fraction of two
-             * integers: a numerator that can be anything, and a denominator that is 
-             * some power of 10.
-             * 
-             * For example, the following numbers are represented by
-             * the corresponding fractions:
-             * 
-             * VALUE    NUMERATOR   DENOMINATOR
-             * 1        1           1
-             * 1.0      10          10
-             * 1.012    1012        1000
-             * 0.04     4           100
-             * 12.01    1201        100
-             * 
-             * So basically, if the magnitude is greater than or equal to one,
-             * the number of digits is the number of digits in the numerator.
-             * If it's less than one, the number of digits is the number of     digits
-             * in the denominator.
-             */
-
-            int[] bits = decimal.GetBits(value);
-            int scalePart = bits[3];
-            int highPart = bits[2];
-            int middlePart = bits[1];
-            int lowPart = bits[0];
-
-            if (value >= 1M || value <= -1M)
-            {
-
-                var num = new decimal(lowPart, middlePart, highPart, false, 0);
-
-                return new Tuple<decimal, int>(num, (scalePart >> 16) & 0x7fff);
-            }
-            else
-            {
-
-                // Accoring to MSDN, the exponent is represented by
-                // bits 16-23 (the 2nd word):
-                // http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx
-                int exponent = (scalePart & 0x00FF0000) >> 16;
-
-                return new Tuple<decimal, int>(lowPart, exponent + 1);
-            }
-        }
     }
 }