فهرست منبع

Add some missing new Math methods in ECMAScript 6 (#539)

Fixes #538
Marko Lahma 7 سال پیش
والد
کامیت
021c01a615

+ 23 - 0
Jint.Tests.Test262/MathTests.cs

@@ -0,0 +1,23 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class MathTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\Math\\trunc")]
+        [MemberData(nameof(SourceFiles), "built-ins\\Math\\trunc", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\Math\\trunc", true, Skip = "Skipped")]
+        protected void Trunc(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+
+        [Theory(DisplayName = "built-ins\\Math\\sign")]
+        [MemberData(nameof(SourceFiles), "built-ins\\Math\\sign", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\Math\\sign", true, Skip = "Skipped")]
+        protected void Sign(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 103 - 110
Jint.Tests.Test262/Test262Test.cs

@@ -1,94 +1,87 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.IO.Enumeration;
 using System.Reflection;
 using Jint.Runtime;
+using Newtonsoft.Json.Linq;
 using Xunit;
 
 namespace Jint.Tests.Test262
 {
-    public class Test262Test
+    public abstract class Test262Test
     {
-        private static string _lastError;
-        private static string[] sources;
-        protected Action<string> Error = s => { _lastError = s; };
-        protected string BasePath;
+        private static readonly string[] Sources;
 
-        public Test262Test()
+        private static readonly string BasePath;
+
+        private static readonly TimeZoneInfo _pacificTimeZone;
+
+        private static readonly Dictionary<string, string> _skipReasons =
+            new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+
+        static Test262Test()
         {
+            //NOTE: The Date tests in test262 assume the local timezone is Pacific Standard Time
+            _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
+
             var assemblyPath = new Uri(typeof(Test262Test).GetTypeInfo().Assembly.CodeBase).LocalPath;
             var assemblyDirectory = new FileInfo(assemblyPath).Directory;
 
             BasePath = assemblyDirectory.Parent.Parent.Parent.FullName;
-        }
 
-        protected void RunTestCode(string code, bool negative)
-        {
-            _lastError = null;
-
-            //NOTE: The Date tests in test262 assume the local timezone is Pacific Standard Time
-            var pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
-            var engine = new Engine(cfg => cfg.LocalTimeZone(pacificTimeZone));
+            string[] files =
+            {
+                @"harness\sta.js",
+                @"harness\assert.js",
+                @"harness\propertyHelper.js",
+                @"harness\compareArray.js",
+                @"harness\decimalToHexString.js",
+            };
 
-            // loading driver
-            if (sources == null)
+            Sources = new string[files.Length];
+            for (var i = 0; i < files.Length; i++)
             {
-                string[] files =
-                {
-                    @"harness\sta.js",
-                    @"harness\assert.js",
-                    @"harness\propertyHelper.js",
-                };
+                Sources[i] = File.ReadAllText(Path.Combine(BasePath, files[i]));
+            }
 
-                sources = new string[files.Length];
-                for (var i = 0; i < files.Length; i++)
-                {
-                    sources[i] = File.ReadAllText(Path.Combine(BasePath, files[i]));
-                }
+            var content = File.ReadAllText(Path.Combine(BasePath, "test/skipped.json"));
+            var doc = JArray.Parse(content);
+            foreach (var entry in doc.Values<JObject>())
+            {
+                _skipReasons[entry["source"].Value<string>()] = entry["reason"].Value<string>();
             }
+        }
 
-            for (int i = 0; i < sources.Length; ++i)
+        protected void RunTestCode(string code, bool strict)
+        {
+            var engine = new Engine(cfg => cfg
+                .LocalTimeZone(_pacificTimeZone)
+                .Strict(strict));
+
+            for (int i = 0; i < Sources.Length; ++i)
             {
-                engine.Execute(sources[i]);
+                engine.Execute(Sources[i]);
             }
 
-            if (negative)
+            string lastError = null;
+            try
             {
-                try
-                {
-                    engine.Execute(code);
-                    Assert.True(_lastError != null);
-                    Assert.False(true);
-                }
-                catch
-                {
-                    // exception is expected
-                }
+                engine.Execute(code);
             }
-            else
+            catch (JavaScriptException j)
             {
-                try
-                {
-                    engine.Execute(code);
-                }
-                catch (JavaScriptException j)
-                {
-                    _lastError = TypeConverter.ToString(j.Error);
-                }
-                catch (Exception e)
-                {
-                    _lastError = e.ToString();
-                }
-
-                Assert.Null(_lastError);
+                lastError = TypeConverter.ToString(j.Error);
+            }
+            catch (Exception e)
+            {
+                lastError = e.ToString();
             }
+
+            Assert.Null(lastError);
         }
 
-        [Theory(DisplayName = "Test256")]
-        [MemberData(nameof(SourceFiles), false)]
-        [MemberData(nameof(SourceFiles), true, Skip = "Skipped")]
-        protected void RunTest(SourceFile sourceFile)
+        protected void RunTestInternal(SourceFile sourceFile)
         {
             var fullName = sourceFile.FullPath;
             if (!File.Exists(fullName))
@@ -97,76 +90,76 @@ namespace Jint.Tests.Test262
             }
 
             string code = File.ReadAllText(fullName);
-            var negative = code.Contains("@negative");
-
-            RunTestCode(code, negative);
+            RunTestCode(code);
         }
 
-        public static IEnumerable<object[]> SourceFiles(bool skipped)
+        private void RunTestCode(string code)
         {
-            var assemblyPath = new Uri(typeof(Test262Test).GetTypeInfo().Assembly.CodeBase).LocalPath;
-            var assemblyDirectory = new FileInfo(assemblyPath).Directory;
-
-            var localPath = assemblyDirectory.Parent.Parent.Parent.FullName;
-
-            // which tests to include, narrower scope and selectively adding tests
-            var paths = new[]
+            if (code.IndexOf("onlyStrict", StringComparison.Ordinal) < 0)
             {
-                "built-ins\\Map"
-            };
+                RunTestCode(code, strict: false);
+            }
 
+            if (code.IndexOf("noStrict", StringComparison.Ordinal) < 0)
+            {
+                RunTestCode(code, strict: true);
+            }
+        }
 
+        public static IEnumerable<object[]> SourceFiles(string pathPrefix, bool skipped)
+        {
             var results = new List<object[]>();
-            var fixturesPath = Path.Combine(localPath, "test");
-            foreach (var path in paths)
+            var fixturesPath = Path.Combine(BasePath, "test");
+            var searchPath = Path.Combine(fixturesPath, pathPrefix);
+            var files = Directory.GetFiles(searchPath, "*", SearchOption.AllDirectories);
+
+            foreach (var file in files)
             {
-                var searchPath = Path.Combine(fixturesPath, path);
-                var files = Directory.GetFiles(searchPath, "*", SearchOption.AllDirectories);
+                var name = file.Substring(fixturesPath.Length + 1).Replace("\\", "/");
+                bool skip = _skipReasons.TryGetValue(name, out var reason);
 
-                foreach (var file in files)
-                {
-                    var sourceFile = new SourceFile(
-                        path + file.Replace(searchPath, ""),
-                        file,
-                        skip: false,
-                        reason: "");
+                var sourceFile = new SourceFile(
+                    name,
+                    file,
+                    skip,
+                    reason);
 
-                    if (skipped == sourceFile.Skip)
+                if (skipped == sourceFile.Skip)
+                {
+                    results.Add(new object[]
                     {
-                        results.Add(new object[]
-                        {
-                            sourceFile
-                        });
-                    }
+                        sourceFile
+                    });
                 }
             }
 
             return results;
         }
+    }
 
-        public class SourceFile
+    public class SourceFile
+    {
+        public SourceFile(
+            string source,
+            string fullPath,
+            bool skip,
+            string reason)
         {
-            public SourceFile(
-                string source,
-                string fullPath,
-                bool skip,
-                string reason)
-            {
-                Skip = skip;
-                Source = source;
-                Reason = reason;
-                FullPath = fullPath;
-            }
+            Skip = skip;
+            Source = source;
+            Reason = reason;
+            FullPath = fullPath;
+        }
 
-            public string Source { get; set; }
-            public bool Skip { get; set; }
-            public string Reason { get; set; }
-            public string FullPath { get; }
+        public string Source { get; }
+        public bool Skip { get; }
+        public string Reason { get; }
+        public string FullPath { get; }
 
-            public override string ToString()
-            {
-                return Source;
-            }
+        public override string ToString()
+        {
+            return Source;
         }
     }
+
 }

+ 18 - 0
Jint.Tests.Test262/test/skipped.json

@@ -0,0 +1,18 @@
+[
+  {
+    "source": "built-ins/Math/sign/name.js",
+    "reason": "name not yet implemented"
+  },
+  {
+    "source": "built-ins/Math/sign/length.js",
+    "reason": "length not yet implemented"
+  },
+  {
+    "source": "built-ins/Math/trunc/name.js",
+    "reason": "name not yet implemented"
+  },
+  {
+    "source": "built-ins/Math/trunc/length.js",
+    "reason": "length not yet implemented"
+  }
+]

+ 6 - 0
Jint.sln

@@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jint.Tests.Ecma", "Jint.Tes
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jint.Tests.CommonScripts", "Jint.Tests.CommonScripts\Jint.Tests.CommonScripts.csproj", "{B815F239-6409-4BA7-9461-18317AA2DBED}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jint.Tests.Test262", "Jint.Tests.Test262\Jint.Tests.Test262.csproj", "{62FFFDBD-AB58-490D-9A50-AA7C53BF0409}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -45,6 +47,10 @@ Global
 		{B815F239-6409-4BA7-9461-18317AA2DBED}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{B815F239-6409-4BA7-9461-18317AA2DBED}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{B815F239-6409-4BA7-9461-18317AA2DBED}.Release|Any CPU.Build.0 = Release|Any CPU
+		{62FFFDBD-AB58-490D-9A50-AA7C53BF0409}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{62FFFDBD-AB58-490D-9A50-AA7C53BF0409}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{62FFFDBD-AB58-490D-9A50-AA7C53BF0409}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{62FFFDBD-AB58-490D-9A50-AA7C53BF0409}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 85 - 1
Jint/Native/Math/MathInstance.cs

@@ -20,7 +20,6 @@ namespace Jint.Native.Math
             math.Extensible = true;
             math.Prototype = engine.Object.PrototypeObject;
 
-            
             return math;
         }
 
@@ -45,6 +44,10 @@ namespace Jint.Native.Math
             FastAddProperty("sqrt", new ClrFunctionInstance(Engine, Sqrt), true, false, true);
             FastAddProperty("tan", new ClrFunctionInstance(Engine, Tan), true, false, true);
 
+            FastAddProperty("trunc", new ClrFunctionInstance(Engine, Truncate), true, false, true);
+            FastAddProperty("sign", new ClrFunctionInstance(Engine, Sign), true, false, true);
+            FastAddProperty("cbrt", new ClrFunctionInstance(Engine, Cbrt), true, false, true);
+
             FastAddProperty("E", System.Math.E, false, false, false);
             FastAddProperty("LN10", System.Math.Log(10), false, false, false);
             FastAddProperty("LN2", System.Math.Log(2), false, false, false);
@@ -650,6 +653,87 @@ namespace Jint.Native.Math
             return System.Math.Tan(x);
         }
 
+        private static JsValue Truncate(JsValue thisObject, JsValue[] arguments)
+        {
+            var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x))
+            {
+                return double.NaN;
+            }
+
+            if (NumberInstance.IsPositiveZero(x) || NumberInstance.IsNegativeZero(x))
+            {
+                return x;
+            }
+
+            if (double.IsPositiveInfinity(x))
+            {
+                return double.PositiveInfinity;
+            }
+
+            if (double.IsNegativeInfinity(x))
+            {
+                return double.NegativeInfinity;
+            }
+
+            return System.Math.Truncate(x);
+        }
+
+        private static JsValue Sign(JsValue thisObject, JsValue[] arguments)
+        {
+            var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x))
+            {
+                return double.NaN;
+            }
+
+            if (NumberInstance.IsPositiveZero(x) || NumberInstance.IsNegativeZero(x))
+            {
+                return x;
+            }
+
+            if (double.IsPositiveInfinity(x))
+            {
+                return 1;
+            }
+
+            if (double.IsNegativeInfinity(x))
+            {
+                return -1;
+            }
+
+            return System.Math.Sign(x);
+        }
+
+        private static JsValue Cbrt(JsValue thisObject, JsValue[] arguments)
+        {
+            var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x))
+            {
+                return double.NaN;
+            }
+            else if (NumberInstance.IsPositiveZero(x) || NumberInstance.IsNegativeZero(x))
+            {
+                return x;
+            }
+            else if (double.IsPositiveInfinity(x))
+            {
+                return double.PositiveInfinity;
+            }
+            else if (double.IsNegativeInfinity(x))
+            {
+                return double.NegativeInfinity;
+            }
+
+            if (System.Math.Sign(x) >= 0)
+            {
+                return System.Math.Pow(x, 1.0/3.0);
+            }
 
+            return -1 * System.Math.Pow(System.Math.Abs(x), 1.0 / 3.0);
+        }
     }
 }

+ 1 - 0
Jint/Native/Number/NumberConstructor.cs

@@ -37,6 +37,7 @@ namespace Jint.Native.Number
             SetOwnProperty("NaN", new PropertyDescriptor(double.NaN, PropertyFlag.AllForbidden));
             SetOwnProperty("NEGATIVE_INFINITY", new PropertyDescriptor(double.NegativeInfinity, PropertyFlag.AllForbidden));
             SetOwnProperty("POSITIVE_INFINITY", new PropertyDescriptor(double.PositiveInfinity, PropertyFlag.AllForbidden));
+            SetOwnProperty("EPSILON", new PropertyDescriptor(double.Epsilon, PropertyFlag.AllForbidden));
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)

+ 2 - 1
appveyor.yml

@@ -13,6 +13,7 @@ test_script:
   - dotnet test .\Jint.Tests\Jint.Tests.csproj -c Release -f netcoreapp2.1
   - dotnet test .\Jint.Tests.CommonScripts\Jint.Tests.CommonScripts.csproj -c Release -f netcoreapp2.1
   - dotnet test .\Jint.Tests.Ecma\Jint.Tests.Ecma.csproj -c Release -f netcoreapp2.1
+  - dotnet test .\Jint.Tests.Test262\Jint.Tests.Test262.csproj -c Release -f netcoreapp2.1
 artifacts:
   - path: 'Jint\**\*.nupkg'
 deploy:  
@@ -39,4 +40,4 @@ deploy:
     api_key:
       secure: JxLWXrsmfG8K7ECxi5dYFsPZERpRLdi3u5W8RSS/auJ5tzqv+PLCZzUB+yeY1V38
     skip_symbols: true
-    artifact: /.*\.nupkg/  
+    artifact: /.*\.nupkg/