Explorar o código

ES6 destructuring (#578)

Marko Lahma %!s(int64=6) %!d(string=hai) anos
pai
achega
4692fc12c5

+ 8 - 0
Jint.Tests.Test262/LanguageTests.cs

@@ -12,6 +12,14 @@ namespace Jint.Tests.Test262
             RunTestInternal(sourceFile);
         }
 
+        [Theory(DisplayName = "language\\destructuring")]
+        [MemberData(nameof(SourceFiles), "language\\destructuring", false)]
+        [MemberData(nameof(SourceFiles), "language\\destructuring", true, Skip = "Skipped")]
+        protected void Destructuring(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+
         [Theory(DisplayName = "language\\expressions\\array")]
         [MemberData(nameof(SourceFiles), "language\\expressions\\array", false)]
         [MemberData(nameof(SourceFiles), "language\\expressions\\array", true, Skip = "Skipped")]

+ 2 - 2
Jint.Tests.Test262/SingleTest.cs

@@ -23,9 +23,9 @@ namespace Jint.Tests.Test262
         [RunnableInDebugOnly]
         public void TestSingle()
         {
-            const string Target = @"built-ins/Math/pow/applying-the-exp-operator_A19.js";
+            const string Target = @"language/statements/for/dstr-const-ary-init-iter-close.js";
             //const string Target = @"built-ins/Array/from/calling-from-valid-2.js";
-            var sourceFile = SourceFiles("built-ins", false)
+            var sourceFile = SourceFiles("language/statements", false)
                 .SelectMany(x => x)
                 .Cast<SourceFile>()
                 .First(x => x.Source == Target);

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

@@ -196,10 +196,6 @@ namespace Jint.Tests.Test262
                                 skip = true;
                                 reason = "generators not implemented";
                                 break;
-                            case "destructuring-binding":
-                                skip = true;
-                                reason = "destructuring-binding not implemented";
-                                break;
                             case "let":
                                 skip = true;
                                 reason = "let not implemented";

+ 118 - 8
Jint.Tests.Test262/test/skipped.json

@@ -337,10 +337,6 @@
     "source": "language/statements/for/head-let-fresh-binding-per-iteration.js",
     "reason": "let/const not implemented"
   },
-  {
-    "source": "language/statements/for/head-lhs-let.js",
-    "reason": "Esprima problem"
-  },
   {
     "source": "language/statements/for-in/head-var-bound-names-dup.js",
     "reason": "destructing not implemented"
@@ -357,10 +353,6 @@
     "source": "language/rest-parameters/object-pattern.js",
     "reason": "destructing not implemented"
   },
-  {
-    "source": "language/expressions/call/trailing-comma.js",
-    "reason": "Esprima problem"
-  },
   {
     "source": "built-ins/Math/pow/int32_min-exponent.js",
     "reason": "const not implemented"
@@ -385,6 +377,124 @@
     "source": "language/types/number/8.5.1.js",
     "reason": "C# can't distinguish 1.797693134862315808e+308 and 1.797693134862315708145274237317e+308"
   },
+  {
+    "source": "language/statements/for/dstr-const-ary-ptrn-elem-id-init-fn-name-arrow.js",
+    "reason": "arrow functions not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-const-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-let-ary-ptrn-elem-id-init-fn-name-arrow.js",
+    "reason": "arrow functions not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-let-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-let-obj-ptrn-id-init-fn-name-arrow.js",
+    "reason": "arrow functions not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-const-obj-ptrn-id-init-fn-name-arrow.js",
+    "reason": "arrow functions not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-var-ary-ptrn-elem-id-init-fn-name-arrow.js",
+    "reason": "arrow functions not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-var-obj-ptrn-id-init-fn-name-arrow.js",
+    "reason": "arrow functions not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-let-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-const-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-var-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-var-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-const-ary-ptrn-rest-obj-prop-id.js",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-var-ary-ptrn-rest-obj-prop-id.js",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-let-ary-ptrn-rest-obj-prop-id.js",
+    "reason": "let not implemented"
+  },
+  
+  
+  
+  
+  // Esprima problems
+  
+  {
+    "source": "language/statements/for/dstr-var-obj-ptrn-rest-getter.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/statements/for/dstr-var-obj-ptrn-rest-skip-non-enumerable",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/statements/for/dstr-var-obj-ptrn-rest-val-obj.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/statements/for/dstr-const-obj-ptrn-rest-getter.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/statements/for/dstr-const-obj-ptrn-rest-skip-non-enumerable.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/statements/for/dstr-const-obj-ptrn-rest-skip-non-enumerable.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/statements/for/dstr-const-obj-ptrn-rest-val-obj.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/statements/for/dstr-let-obj-ptrn-rest-getter.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/statements/for/dstr-let-obj-ptrn-rest-skip-non-enumerable.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/statements/for/dstr-let-obj-ptrn-rest-val-obj.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/statements/for/dstr-var-obj-ptrn-rest-skip-non-enumerable.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/call/trailing-comma.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/statements/for/head-lhs-let.js",
+    "reason": "Esprima problem"
+  },
   {
     "source": "language/white-space/mongolian-vowel-separator-eval.js",
     "reason": "Esprima problem"

+ 6 - 4
Jint/Engine.cs

@@ -832,11 +832,13 @@ namespace Jint
                     for (var j = 0; j < declarationsCount; j++)
                     {
                         var d = variableDeclaration.Declarations[j];
-                        var dn = ((Identifier) d.Id).Name;
-                        var varAlreadyDeclared = env.HasBinding(dn);
-                        if (!varAlreadyDeclared)
+                        if (d.Id is Identifier id1)
                         {
-                            env.CreateMutableBinding(dn, Undefined.Instance);
+                            var varAlreadyDeclared = env.HasBinding(id1.Name);
+                            if (!varAlreadyDeclared)
+                            {
+                                env.CreateMutableBinding(id1.Name, Undefined.Instance);
+                            }
                         }
                     }
                 }

+ 1 - 1
Jint/Native/Array/ArrayConstructor.cs

@@ -128,7 +128,7 @@ namespace Jint.Native.Array
             return instance;
         }
 
-        private sealed class ArrayProtocol : IteratorProtocol
+        internal sealed class ArrayProtocol : IteratorProtocol
         {
             private readonly JsValue _thisArg;
             private readonly ArrayInstance _instance;

+ 16 - 1
Jint/Native/Function/FunctionInstance.cs

@@ -43,7 +43,10 @@ namespace Jint.Native.Function
             string objectClass)
             : base(engine, objectClass)
         {
-            _name = new PropertyDescriptor(name, PropertyFlag.Configurable);
+            if (!string.IsNullOrWhiteSpace(name))
+            {
+                _name = new PropertyDescriptor(name, PropertyFlag.Configurable);
+            }
             _formalParameters = parameters;
             _scope = scope;
             _strict = strict;
@@ -213,5 +216,17 @@ namespace Jint.Native.Function
 
             base.RemoveOwnProperty(propertyName);
         }
+
+        internal void SetFunctionName(string name, bool throwIfExists = false)
+        {
+            if (!HasOwnProperty("name"))
+            {
+                _name = new PropertyDescriptor(name, PropertyFlag.Configurable);
+            }
+            else if (throwIfExists)
+            {
+                ExceptionHelper.ThrowError(_engine, "cannot set name");
+            }
+        }
     }
 }

+ 0 - 5
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -48,11 +48,6 @@ namespace Jint.Native.Function
 
             _prototype = new PropertyDescriptor(proto, PropertyFlag.OnlyWritable);
 
-            if (_function._name != null)
-            {
-                DefineOwnProperty("name", new PropertyDescriptor(_function._name, PropertyFlag.None), false);
-            }
-
             if (strict)
             {
                 var thrower = engine.Function.ThrowTypeError;

+ 0 - 1
Jint/Native/Number/Dtoa/FastDtoa.cs

@@ -30,7 +30,6 @@
 
 using System;
 using System.Diagnostics;
-using System.Threading;
 using Jint.Runtime;
 
 namespace Jint.Native.Number.Dtoa

+ 23 - 5
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -271,6 +271,7 @@ using Jint.Native.Function;
                 var jsValue = arguments.Length == 0 ? Undefined : arguments[0];
                 jsValue = HandleAssignmentPatternIfNeeded(functionDeclaration, jsValue, 0);
                 jsValue = HandleRestPatternIfNeeded(_engine, functionDeclaration, arguments, 0, jsValue);
+                HandleObjectPatternIfNeeded(_engine, functionDeclaration, jsValue, 0);
 
                 var binding = new Binding(jsValue, false, true);
                 _set = true;
@@ -301,6 +302,7 @@ using Jint.Native.Function;
                 {
                     jsValue = HandleRestPatternIfNeeded(_engine, functionDeclaration, arguments, i, jsValue);
                 }
+                jsValue = HandleObjectPatternIfNeeded(_engine, functionDeclaration, jsValue, 0);
 
                 if (empty || !TryGetValue(argName, out var existing))
                 {
@@ -328,6 +330,20 @@ using Jint.Native.Function;
                 }
             }
         }
+        
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static JsValue HandleObjectPatternIfNeeded(Engine engine, IFunction functionDeclaration, JsValue jsValue, int index)
+        {
+            if (functionDeclaration.Params[index] is ObjectPattern op)
+            {
+                if (jsValue.IsNullOrUndefined())
+                {
+                    ExceptionHelper.ThrowTypeError(engine, "Cannot destructure 'undefined' or 'null'.");
+                }
+            }
+
+            return jsValue;
+        }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static JsValue HandleAssignmentPatternIfNeeded(IFunction functionDeclaration, JsValue jsValue, int index)
@@ -378,12 +394,14 @@ using Jint.Native.Function;
                 for (var j = 0; j < declarationsCount; j++)
                 {
                     var d = variableDeclaration.Declarations[j];
-                    var dn = ((Identifier) d.Id).Name;
-
-                    if (!ContainsKey(dn))
+                    if (d.Id is Identifier id)
                     {
-                        var binding = new Binding(Undefined, canBeDeleted: false, mutable: true);
-                        SetItem(dn, binding);
+                        var dn = id.Name;
+                        if (!ContainsKey(dn))
+                        {
+                            var binding = new Binding(Undefined, canBeDeleted: false, mutable: true);
+                            SetItem(dn, binding);
+                        }
                     }
                 }
             }

+ 265 - 0
Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs

@@ -0,0 +1,265 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Native.Array;
+using Jint.Native.Function;
+using Jint.Native.Iterator;
+using Jint.Runtime.Environments;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class BindingPatternAssignmentExpression : JintExpression
+    {
+        private readonly BindingPattern _pattern;
+        private readonly JintExpression _right;
+
+        public BindingPatternAssignmentExpression(
+            Engine engine, 
+            AssignmentExpression expression) : base(engine, expression)
+        {
+            _pattern = (BindingPattern) expression.Left;
+            _right = Build(engine, expression.Right);
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var rightValue = _right.GetValue();
+            ProcessPatterns(_engine, _pattern, rightValue);
+            return JsValue.Undefined;
+        }
+
+        internal static void ProcessPatterns(Engine engine, BindingPattern pattern, JsValue argument)
+        {
+            if (pattern is ArrayPattern ap)
+            {
+                HandleArrayPattern(engine, ap, argument);
+            }
+            else if (pattern is ObjectPattern op)
+            {
+                HandleObjectPattern(engine, op, argument);
+            }
+        }
+
+        private static void HandleArrayPattern(Engine engine, ArrayPattern pattern, JsValue argument)
+        {
+            bool ConsumeFromIterator(IIterator it, out JsValue value, out bool done)
+            {
+                var item = it.Next();
+                value = JsValue.Undefined;
+                done = false;
+
+                if (item.TryGetValue("done", out var d) && d.AsBoolean())
+                {
+                    done = true;
+                    return false;
+                }
+
+                if (!item.TryGetValue("value", out value))
+                {
+                    return false;
+                }
+
+                return true;
+            }
+
+            var obj = TypeConverter.ToObject(engine, argument);
+
+            ArrayPrototype.ArrayOperations arrayOperations = null;
+            IIterator iterator = null;
+            if (obj.IsArrayLike)
+            {
+                arrayOperations = ArrayPrototype.ArrayOperations.For(obj);
+            }
+            else
+            {
+                if (!obj.TryGetIterator(engine, out iterator))
+                {
+                    ExceptionHelper.ThrowTypeError(engine);
+                    return;
+                }
+            }
+
+            bool iterationEnded = false;
+            for (uint i = 0; i < pattern.Elements.Count; i++)
+            {
+                var left = pattern.Elements[(int) i];
+                if (left is Identifier identifier)
+                {
+                    JsValue value;
+                    if (arrayOperations != null)
+                    {
+                        arrayOperations.TryGetValue(i, out value);
+                    }
+                    else
+                    {
+                        if (!ConsumeFromIterator(iterator, out value, out iterationEnded))
+                        {
+                            break;
+                        }
+                    }
+                    AssignToIdentifier(engine, identifier.Name, value);
+                }
+                else if (left is BindingPattern bindingPattern)
+                {
+                    JsValue value;
+                    if (arrayOperations != null)
+                    {
+                        arrayOperations.TryGetValue(i, out value);
+                    }
+                    else
+                    {
+                        value = iterator.Next();
+                    }
+                    ProcessPatterns(engine, bindingPattern, value);
+                }
+                else if (left is ArrayPatternElement arrayPatternElement)
+                {
+                    if (arrayPatternElement is RestElement restElement)
+                    {
+                        ArrayInstance array;
+                        if (arrayOperations != null)
+                        {
+                            var length = arrayOperations.GetLength();
+                            array = engine.Array.ConstructFast(length - i);
+                            for (uint j = i; j < length; ++j)
+                            {
+                                arrayOperations.TryGetValue(j, out var indexValue);
+                                array.SetIndexValue(j - i, indexValue, updateLength: false);
+                            }
+                        }
+                        else
+                        {
+                            array = engine.Array.ConstructFast(0);
+                            var protocol = new ArrayConstructor.ArrayProtocol(engine, obj, array, iterator, null);
+                            protocol.Execute();
+                        }
+
+                        if (restElement.Argument is Identifier leftIdentifier)
+                        {
+                            AssignToIdentifier(engine, leftIdentifier.Name, array);
+                        }
+                        else if (restElement.Argument is BindingPattern bp)
+                        {
+                            ProcessPatterns(engine, bp, array);
+                        }
+                    }
+                    else if (arrayPatternElement is AssignmentPattern assignmentPattern)
+                    {
+                        JsValue value;
+                        if (arrayOperations != null)
+                        {
+                            arrayOperations.TryGetValue(i, out value);
+                        }
+                        else
+                        {
+                            ConsumeFromIterator(iterator, out value, out iterationEnded);
+                        }
+
+                        if (value.IsUndefined()
+                            && assignmentPattern.Right is Expression expression)
+                        {
+                            var jintExpression = Build(engine, expression);
+                            value = jintExpression.GetValue();
+                        }
+
+                        if (assignmentPattern.Left is Identifier leftIdentifier)
+                        {
+                            if (assignmentPattern.Right is FunctionExpression)
+                            {
+                                ((FunctionInstance) value).SetFunctionName(leftIdentifier.Name);
+                            }
+                            AssignToIdentifier(engine, leftIdentifier.Name, value);
+                        }
+                        else if (assignmentPattern.Left is BindingPattern bp)
+                        {
+                            ProcessPatterns(engine, bp, value);
+                        }
+                    }
+                    else
+                    {
+                        ExceptionHelper.ThrowArgumentOutOfRangeException("pattern", "Unable to determine how to handle array pattern element");
+                        break;
+                    }
+                }
+            }
+
+            if (!iterationEnded)
+            {
+                iterator?.Return();
+            }
+        }
+
+        private static void HandleObjectPattern(Engine engine, ObjectPattern pattern, JsValue argument)
+        {
+            var source = TypeConverter.ToObject(engine, argument);
+            for (uint i = 0; i < pattern.Properties.Count; i++)
+            {
+                var left = pattern.Properties[(int) i];
+                string sourceKey;
+                Identifier identifier = left.Key as Identifier;
+                if (identifier == null)
+                {
+                    var keyExpression = Build(engine, left.Key);
+                    sourceKey = TypeConverter.ToPropertyKey(keyExpression.GetValue());
+                }
+                else
+                {
+                    sourceKey = identifier.Name;
+                }
+
+                source.TryGetValue(sourceKey, out var value);
+                if (left.Value is AssignmentPattern assignmentPattern)
+                {
+                    if (value.IsUndefined() && assignmentPattern.Right is Expression expression)
+                    {
+                        var jintExpression = Build(engine, expression);
+                        value = jintExpression.GetValue();
+                    }
+                    
+                    if (assignmentPattern.Left is BindingPattern bp)
+                    {
+                        ProcessPatterns(engine, bp, value);
+                        continue;
+                    }
+                    
+                    var target = assignmentPattern.Left as Identifier ?? identifier;
+                    if (assignmentPattern.Right is FunctionExpression)
+                    {
+                        ((FunctionInstance) value).SetFunctionName(target.Name);
+                    }
+                    
+                    AssignToIdentifier(engine, target.Name, value);
+                }
+                else if (left.Value is BindingPattern bindingPattern)
+                {
+                    ProcessPatterns(engine, bindingPattern, value);
+                }
+                else
+                {
+                    var target = left.Value as Identifier ?? identifier;
+                    AssignToIdentifier(engine, target.Name, value);
+                }
+            }
+        }
+
+        private static void AssignToIdentifier(Engine engine,
+            string name,
+            JsValue rval)
+        {
+            var env = engine.ExecutionContext.LexicalEnvironment;
+            var strict = StrictModeScope.IsStrictModeCode;
+            if (LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
+                env,
+                name,
+                strict,
+                out var environmentRecord,
+                out _))
+            {
+                environmentRecord.SetMutableBinding(name, rval, strict);
+            }
+            else
+            {
+                env._record.CreateMutableBinding(name, rval);
+            }
+        }
+    }
+}

+ 30 - 7
Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs

@@ -20,7 +20,15 @@ namespace Jint.Runtime.Interpreter.Expressions
         {
             if (expression.Operator == AssignmentOperator.Assign)
             {
-                return new Assignment(engine, expression);
+                if (expression.Left is Expression)
+                {
+                    return new SimpleAssignmentExpression(engine, expression);
+                }
+
+                if (expression.Left is BindingPattern)
+                {
+                    return new BindingPatternAssignmentExpression(engine, expression);
+                }
             }
 
             return new JintAssignmentExpression(engine, expression);
@@ -122,21 +130,29 @@ namespace Jint.Runtime.Interpreter.Expressions
             return lval;
         }
 
-        internal class Assignment : JintExpression
+        internal sealed class SimpleAssignmentExpression : JintExpression
         {
             private readonly JintExpression _left;
             private readonly JintExpression _right;
 
             private readonly JintIdentifierExpression _leftIdentifier;
             private readonly bool _evalOrArguments;
+            private readonly ArrayPattern _arrayPattern;
 
-            public Assignment(Engine engine, AssignmentExpression expression) : base(engine, expression)
+            public SimpleAssignmentExpression(Engine engine, AssignmentExpression expression) : base(engine, expression)
             {
-                _left = Build(engine, (Expression) expression.Left);
-                _right = Build(engine, expression.Right);
+                if (expression.Left is ArrayPattern arrayPattern)
+                {
+                    _arrayPattern = arrayPattern;
+                }
+                else
+                {
+                    _left = Build(engine, (Expression) expression.Left);
+                    _leftIdentifier = _left as JintIdentifierExpression;
+                    _evalOrArguments = _leftIdentifier?._expressionName == "eval" || _leftIdentifier?._expressionName == "arguments";
+                }
 
-                _leftIdentifier = _left as JintIdentifierExpression;
-                _evalOrArguments = _leftIdentifier?._expressionName == "eval" || _leftIdentifier?._expressionName == "arguments";
+                _right = Build(engine, expression.Right);
             }
 
             protected override object EvaluateInternal()
@@ -146,6 +162,13 @@ namespace Jint.Runtime.Interpreter.Expressions
                 {
                     rval = AssignToIdentifier(_engine, _leftIdentifier, _right, _evalOrArguments);
                 }
+                else if (_arrayPattern != null)
+                {
+                    foreach (var element in _arrayPattern.Elements)
+                    {
+                        AssignToIdentifier(_engine, _leftIdentifier, _right, false);
+                    }
+                }
 
                 return rval ?? SetValue();
             }

+ 1 - 1
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -124,7 +124,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     return new JintTaggedTemplateExpression(engine, (TaggedTemplateExpression) expression);
 
                 default:
-                    ExceptionHelper.ThrowArgumentOutOfRangeException();
+                    ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(expression), $"unsupported language element '{expression.Type}'");
                     return null;
             }
         }

+ 10 - 0
Jint/Runtime/Interpreter/JintFunctionDefinition.cs

@@ -59,6 +59,16 @@ namespace Jint.Runtime.Interpreter
                     }
                     _hasRestParameter = true;
                 }
+                else if (node is BindingPattern)
+                {
+                    names[i] = "";
+                }
+                else
+                {
+                    ExceptionHelper.ThrowArgumentOutOfRangeException(
+                        nameof(functionDeclaration),
+                        "Unable to determine how to handle parameter of type " + node.GetType());
+                }
             }
 
             return names;

+ 28 - 8
Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs

@@ -14,6 +14,7 @@ namespace Jint.Runtime.Interpreter.Statements
         private sealed class ResolvedDeclaration
         {
             internal JintExpression Left;
+            internal BindingPattern LeftPattern;
             internal JintExpression Init;
             internal JintIdentifierExpression LeftIdentifier;
             internal bool EvalOrArguments;
@@ -30,17 +31,29 @@ namespace Jint.Runtime.Interpreter.Statements
             for (var i = 0; i < _declarations.Length; i++)
             {
                 var declaration = _statement.Declarations[i];
-                var left = declaration.Init != null
-                    ? JintExpression.Build(_engine, (Expression) declaration.Id)
-                    : null;
-                var init = declaration.Init != null
-                    ? JintExpression.Build(_engine, declaration.Init)
-                    : null;
+
+                JintExpression left = null;
+                JintExpression init = null;
+                BindingPattern bindingPattern = null;
+                if (declaration.Init != null)
+                {
+                    if (declaration.Id is Expression expression)
+                    {
+                        left = JintExpression.Build(_engine, expression);
+                    }
+                    else if (declaration.Id is BindingPattern bp)
+                    {
+                        bindingPattern = bp;
+                    }
+                        
+                    init = JintExpression.Build(_engine, declaration.Init);
+                }
 
                 var leftIdentifier = left as JintIdentifierExpression;
                 _declarations[i] = new ResolvedDeclaration
                 {
                     Left = left,
+                    LeftPattern = bindingPattern,
                     LeftIdentifier = leftIdentifier,
                     EvalOrArguments = leftIdentifier?._expressionName == "eval" || leftIdentifier?._expressionName == "arguments",
                     Init = init
@@ -56,8 +69,15 @@ namespace Jint.Runtime.Interpreter.Statements
                 var declaration = declarations[i];
                 if (declaration.Init != null)
                 {
-                    if (declaration.LeftIdentifier == null
-                        || JintAssignmentExpression.Assignment.AssignToIdentifier(
+                    if (declaration.LeftPattern != null)
+                    {
+                        BindingPatternAssignmentExpression.ProcessPatterns(
+                            _engine,
+                            declaration.LeftPattern,
+                            declaration.Init.GetValue());
+                    }
+                    else if (declaration.LeftIdentifier == null
+                        || JintAssignmentExpression.SimpleAssignmentExpression.AssignToIdentifier(
                             _engine,
                             declaration.LeftIdentifier,
                             declaration.Init,

+ 1 - 1
README.md

@@ -156,7 +156,7 @@ ES6 features which are being implemented:
 - [ ] [classes](https://github.com/lukehoban/es6features/blob/master/README.md#classes)
 - [ ] [enhanced object literals](https://github.com/lukehoban/es6features/blob/master/README.md#enhanced-object-literals)
 - [x] [template strings](https://github.com/lukehoban/es6features/blob/master/README.md#template-strings)
-- [ ] [destructuring](https://github.com/lukehoban/es6features/blob/master/README.md#destructuring)
+- [x] [destructuring](https://github.com/lukehoban/es6features/blob/master/README.md#destructuring)
 - [x] [default + rest + spread](https://github.com/lukehoban/es6features/blob/master/README.md#default--rest--spread)
 - [ ] [let + const](https://github.com/lukehoban/es6features/blob/master/README.md#let--const)
 - [x] [iterators + for..of](https://github.com/lukehoban/es6features/blob/master/README.md#iterators--forof)