Browse Source

ES6 for-of (#577)

* skip tests asserting caller presence
* adding iterator for arguments
* upgrade supporting libraries
Marko Lahma 5 years ago
parent
commit
f5fd44b732

+ 3 - 3
Jint.Benchmark/Jint.Benchmark.csproj

@@ -24,9 +24,9 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
     <PackageReference Include="Jurassic" Version="3.0.0" />
-    <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
-    <PackageReference Include="NiL.JS.NetCore" Version="2.5.1327" />
+    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+    <PackageReference Include="NiL.JS.NetCore" Version="2.5.1419" />
   </ItemGroup>
 </Project>

+ 1 - 1
Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj

@@ -9,7 +9,7 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />

+ 1 - 1
Jint.Tests.Ecma/Jint.Tests.Ecma.csproj

@@ -6,7 +6,7 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />

+ 14 - 14
Jint.Tests.Ecma/TestCases/alltests.json

@@ -6855,18 +6855,18 @@
     source: "ch10/10.6/10.6-13-a-3.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller has been deprecated",
     source: "ch10/10.6/10.6-13-b-1-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller has been deprecated",
     source: "ch10/10.6/10.6-13-b-2-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller has been deprecated",
     source: "ch10/10.6/10.6-13-b-3-s.js"
   },
   {
@@ -6885,18 +6885,18 @@
     source: "ch10/10.6/10.6-13-c-3-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller has been deprecated",
     source: "ch10/10.6/10.6-14-1-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller has been deprecated",
     source: "ch10/10.6/10.6-14-b-1-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller has been deprecated",
     source: "ch10/10.6/10.6-14-b-4-s.js"
   },
   {
@@ -17295,8 +17295,8 @@
     source: "ch13/13.2/S13.2.2_A9.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller has been deprecated",
     source: "ch13/13.2/S13.2.3_A1.js"
   },
   {

+ 2 - 2
Jint.Tests.Test262/Jint.Tests.Test262.csproj

@@ -6,8 +6,8 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
-    <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />

+ 1 - 1
Jint.Tests.Test262/Language/Statements/ForOfTests.cs

@@ -4,7 +4,7 @@ namespace Jint.Tests.Test262.Language.Statements
 {
     public class ForOfTests : Test262Test
     {
-        [Theory(Skip = "for of not implemented", DisplayName = "language\\statements\\for-of")]
+        [Theory(DisplayName = "language\\statements\\for-of")]
         [MemberData(nameof(SourceFiles), "language\\statements\\for-of", false)]
         [MemberData(nameof(SourceFiles), "language\\statements\\for-of", true, Skip = "Skipped")]
         protected void ForOf(SourceFile sourceFile)

+ 4 - 0
Jint.Tests.Test262/Test262Test.cs

@@ -247,6 +247,10 @@ namespace Jint.Tests.Test262
                                 skip = true;
                                 reason = "regexp-lookbehind not implemented";
                                 break;
+                            case "TypedArray":
+                                skip = true;
+                                reason = "TypedArray not implemented";
+                                break;
                         }
                     }
                 }

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

@@ -367,6 +367,42 @@
     "source": "language/expressions/assignment/dstr-obj-rest-put-const.js",
     "reason": "const not implemented"
   },
+  {
+    "source": "language/statements/for-of/dstr-obj-rest-put-const.js",
+    "reason": "const not implemented"
+  },
+  {
+    "source": "language/statements/for-of/head-const-bound-names-fordecl-tdz.js",
+    "reason": "const not implemented"
+  },
+  {
+    "source": "language/statements/for-of/head-const-fresh-binding-per-iteration.js",
+    "reason": "const not implemented"
+  },
+  {
+    "source": "language/statements/for-of/dstr-const-ary-ptrn-rest-obj-prop-id.js",
+    "reason": "const not implemented"
+  },
+  {
+    "source": "language/statements/for-of/head-let-bound-names-fordecl-tdz.js",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/statements/for-of/head-let-destructuring.js",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/statements/for-of/head-let-fresh-binding-per-iteration.js",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/statements/for-of/dstr-let-ary-ptrn-rest-obj-prop-id.js",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/statements/for-of/dstr-var-ary-ptrn-rest-obj-prop-id.js",
+    "reason": "let not implemented"
+  },
   {
     "source": "language/expressions/template-literal/tv-line-terminator-sequence.js",
     "reason": "Line feed problems (git, windows, linux)"
@@ -741,6 +777,30 @@
     "source": "language/expressions/object/method-definition/name-prototype-prop.js",
     "reason": "class not implemented"
   },
+  {
+    "source": "language/statements/for-of/dstr-const-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/for-of/dstr-const-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/for-of/dstr-let-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/for-of/dstr-let-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/for-of/dstr-var-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/for-of/dstr-var-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
 
 
   {
@@ -884,4 +944,8 @@
     "source": "language/expressions/assignment/dstr-obj-id-identifier-yield-ident-valid.js",
     "reason": "Esprima problem"
   },
+  {
+    "source": "language/statements/for-of/dstr-obj-id-identifier-yield-ident-valid.js",
+    "reason": "Esprima problem"
+  },
 ]

+ 1 - 1
Jint.Tests/Jint.Tests.csproj

@@ -12,7 +12,7 @@
     <Reference Include="Microsoft.CSharp" Condition=" '$(TargetFramework)' == 'net461' " />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />

+ 8 - 3
Jint/Native/Argument/ArgumentsInstance.cs

@@ -2,10 +2,12 @@
 using System.Threading;
 using Jint.Native.Function;
 using Jint.Native.Object;
+using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Environments;
+using Jint.Runtime.Interop;
 
 namespace Jint.Native.Argument
 {
@@ -85,14 +87,17 @@ namespace Jint.Native.Argument
             // step 13
             if (!_strict)
             {
-                SetOwnProperty(CommonProperties.Callee, new PropertyDescriptor(_func, PropertyFlag.NonEnumerable));
+                DefinePropertyOrThrow(CommonProperties.Callee, new PropertyDescriptor(_func, PropertyFlag.NonEnumerable));
             }
             // step 14
             else
             {
-                DefineOwnProperty(CommonProperties.Caller, _engine._getSetThrower);
-                DefineOwnProperty(CommonProperties.Callee, _engine._getSetThrower);
+                DefinePropertyOrThrow(CommonProperties.Caller, _engine._getSetThrower);
+                DefinePropertyOrThrow(CommonProperties.Callee, _engine._getSetThrower);
             }
+
+            var iteratorFunction = new ClrFunctionInstance(Engine, "iterator", _engine.Array.PrototypeObject.Values, 0, PropertyFlag.Configurable);
+            DefinePropertyOrThrow(GlobalSymbolRegistry.Iterator, new PropertyDescriptor(iteratorFunction, PropertyFlag.Writable | PropertyFlag.Configurable));
         }
 
         public ObjectInstance ParameterMap { get; set; }

+ 2 - 2
Jint/Native/Array/ArrayPrototype.cs

@@ -106,8 +106,8 @@ namespace Jint.Native.Array
 
             return ExceptionHelper.ThrowTypeError<ObjectInstance>(_engine, "cannot construct iterator");
         }
-        
-        private ObjectInstance Values(JsValue thisObj, JsValue[] arguments)
+
+        internal ObjectInstance Values(JsValue thisObj, JsValue[] arguments)
         {
             if (thisObj is ObjectInstance oi && oi.IsArrayLike)
             {

+ 6 - 11
Jint/Native/Iterator/IteratorInstance.cs

@@ -1,5 +1,5 @@
-using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 
 using Jint.Native.Array;
@@ -312,7 +312,7 @@ namespace Jint.Native.Iterator
             {
                 result = IteratorNext();
 
-                if (result.TryGetValue(CommonProperties.Done, out var done) && done.AsBoolean())
+                if (result.TryGetValue(CommonProperties.Done, out var done) && TypeConverter.ToBoolean(done))
                 {
                     return false;
                 }
@@ -354,26 +354,21 @@ namespace Jint.Native.Iterator
 
         internal class StringIterator : IteratorInstance
         {
-            private readonly string _str;
-            private int _position;
-            private bool _closed;
+            private readonly TextElementEnumerator _iterator;
 
             public StringIterator(Engine engine, string str) : base(engine)
             {
-                _str = str;
-                _position = 0;
+                _iterator = StringInfo.GetTextElementEnumerator(str);
             }
 
             public override bool TryIteratorStep(out ObjectInstance nextItem)
             {
-                var length = _str.Length;
-                if (!_closed && _position < length)
+                if (_iterator.MoveNext())
                 {
-                    nextItem = new ValueIteratorPosition(_engine, _str[_position++]);
+                    nextItem = new ValueIteratorPosition(_engine, (string) _iterator.Current);
                     return true;
                 }
 
-                _closed = true;
                 nextItem = KeyValueIteratorPosition.Done;
                 return false;
             }

+ 2 - 5
Jint/Native/Iterator/IteratorProtocol.cs

@@ -37,10 +37,7 @@ namespace Jint.Native.Iterator
                         break;
                     }
 
-                    if (!item.TryGetValue(CommonProperties.Value, out var currentValue))
-                    {
-                        currentValue = JsValue.Undefined;
-                    }
+                    var currentValue = item.Get(CommonProperties.Value);
 
                     ProcessItem(args, currentValue);
                 } while (ShouldContinue);
@@ -109,7 +106,7 @@ namespace Jint.Native.Iterator
                         return;
                     }
 
-                    nextItem.TryGetValue(CommonProperties.Value, out var temp);
+                    var temp = nextItem.Get(CommonProperties.Value);
 
                     skipClose = false;
                     if (!(temp is ObjectInstance oi))

+ 0 - 2
Jint/Options.cs

@@ -3,8 +3,6 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 using System.Reflection;
-using System.Threading;
-using Jint.Constraints;
 using Jint.Native;
 using Jint.Native.Object;
 using Jint.Runtime.Interop;

+ 2 - 2
Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs

@@ -186,7 +186,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                                     break;
                                 }
 
-                                item.TryGetValue(CommonProperties.Value, out var value);
+                                var value = item.Get(CommonProperties.Value);
                                 array.SetIndexValue(index++, value, updateLength: false);
                             } while (true);
 
@@ -199,7 +199,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         }
                         else if (restElement.Argument is BindingPattern bp)
                         {
-                            ProcessPatterns(engine, bp, array, checkReference);
+                            ProcessPatterns(engine, bp, array, checkReference: false);
                         }                    
                         else
                         {

+ 140 - 0
Jint/Runtime/Interpreter/Statements/JintForOfStatement.cs

@@ -0,0 +1,140 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Native.Iterator;
+using Jint.Runtime.Interpreter.Expressions;
+using Jint.Runtime.References;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    ///     https://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements
+    /// </summary>
+    internal sealed class JintForOfStatement : JintStatement<ForOfStatement>
+    {
+        private JintStatement _body;
+        private JintExpression _identifier;
+        private BindingPattern _leftPattern;
+
+        private JintExpression _right;
+
+        public JintForOfStatement(Engine engine, ForOfStatement statement) : base(engine, statement)
+        {
+            _initialized = false;
+        }
+
+        protected override void Initialize()
+        {
+            if (_statement.Left is VariableDeclaration variableDeclaration)
+            {
+                var element = variableDeclaration.Declarations[0].Id;
+                if (element is BindingPattern bindingPattern)
+                {
+                    _leftPattern = bindingPattern;
+                }
+                else
+                {
+                    _identifier = JintExpression.Build(_engine, (Identifier) element);
+                }
+            }
+            else if (_statement.Left is BindingPattern bindingPattern)
+            {
+                _leftPattern = bindingPattern;
+            }
+            else
+            {
+                _identifier = JintExpression.Build(_engine, (Expression) _statement.Left);
+            }
+
+            _body = Build(_engine, _statement.Body);
+            _right = JintExpression.Build(_engine, _statement.Right);
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            var experValue = _right.GetValue();
+
+            if (!(experValue is IIterator iterator))
+            {
+                var obj = TypeConverter.ToObject(_engine, experValue);
+                obj.TryGetIterator(_engine, out iterator);
+            }
+
+            if (iterator is null)
+            {
+                return ExceptionHelper.ThrowTypeError<Completion>(_engine, _identifier + " is not iterable");
+            }
+
+            var completionType = CompletionType.Normal;
+            var v = JsValue.Undefined;
+            var close = false;
+            try
+            {
+                do
+                {
+                    iterator.TryIteratorStep(out var item);
+                    var done = item.Get(CommonProperties.Done);
+                    if (TypeConverter.ToBoolean(done))
+                    {
+                        // we can close after checks pass
+                        close = true;
+                        break;
+                    }
+
+                    var currentValue = item.Get(CommonProperties.Value);
+
+                    // we can close after checks pass
+                    close = true;
+
+                    if (_leftPattern != null)
+                    {
+                        BindingPatternAssignmentExpression.ProcessPatterns(
+                            _engine,
+                            _leftPattern,
+                            currentValue,
+                            checkReference: !(_statement.Left is VariableDeclaration));
+                    }
+                    else
+                    {
+                        var varRef = (Reference) _identifier.Evaluate();
+                        _engine.PutValue(varRef, currentValue);
+                    }
+
+                    var stmt = _body.Execute();
+
+                    if (!ReferenceEquals(stmt.Value, null))
+                    {
+                        v = stmt.Value;
+                    }
+
+                    if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == _statement?.LabelSet?.Name))
+                    {
+                        return new Completion(CompletionType.Normal, stmt.Value, null, Location);
+                    }
+
+                    if (stmt.Type != CompletionType.Continue || ((stmt.Identifier != null) && stmt.Identifier != _statement?.LabelSet?.Name))
+                    {
+                        if (stmt.Type != CompletionType.Normal)
+                        {
+                            return stmt;
+                        }
+                    }
+                    
+                } while (true);
+            }
+            catch
+            {
+                completionType = CompletionType.Throw;
+                throw;
+            }   
+            finally
+            {
+                if (close)
+                {
+                    iterator.Close(completionType);
+                }
+            }
+
+            return new Completion(CompletionType.Normal, v, null, Location);
+        }
+    }
+}

+ 3 - 4
Jint/Runtime/Interpreter/Statements/JintForStatement.cs

@@ -64,15 +64,14 @@ namespace Jint.Runtime.Interpreter.Statements
                     v = stmt.Value;
                 }
 
-                var stmtType = stmt.Type;
-                if (stmtType == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == _statement?.LabelSet?.Name))
+                if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == _statement?.LabelSet?.Name))
                 {
                     return new Completion(CompletionType.Normal, stmt.Value, null, Location);
                 }
 
-                if (stmtType != CompletionType.Continue || ((stmt.Identifier != null) && stmt.Identifier != _statement?.LabelSet?.Name))
+                if (stmt.Type != CompletionType.Continue || ((stmt.Identifier != null) && stmt.Identifier != _statement?.LabelSet?.Name))
                 {
-                    if (stmtType != CompletionType.Normal)
+                    if (stmt.Type != CompletionType.Normal)
                     {
                         return stmt;
                     }

+ 3 - 0
Jint/Runtime/Interpreter/Statements/JintStatement.cs

@@ -90,6 +90,9 @@ namespace Jint.Runtime.Interpreter.Statements
                 case Nodes.ForInStatement:
                     return new JintForInStatement(engine, (ForInStatement) statement);
 
+                case Nodes.ForOfStatement:
+                    return new JintForOfStatement(engine, (ForOfStatement) statement);
+
                 case Nodes.IfStatement:
                     return new JintIfStatement(engine, (IfStatement) statement);