Browse Source

Fixing For In statement evaluation

Sebastien Ros 12 years ago
parent
commit
4c13a5ea1c

+ 13 - 0
Jint.Tests/Runtime/EngineTests.cs

@@ -343,6 +343,19 @@ namespace Jint.Tests.Runtime
             ");
             ");
         }
         }
 
 
+        [Fact]
+        public void ForInStatement()
+        {
+            RunTest(@"
+                var x, y, z, str = '';
+                for(z in this) {
+                    str += z;
+                }
+                
+                assert(str == 'xyzstr');
+            ");
+        }
+
         /*
         /*
                         [Fact]
                         [Fact]
                         public void ()
                         public void ()

+ 73 - 31
Jint/Engine.cs

@@ -49,14 +49,14 @@ namespace Jint
             Date = new DateConstructor(this);
             Date = new DateConstructor(this);
             Math = MathInstance.CreateMathObject(this, RootObject);
             Math = MathInstance.CreateMathObject(this, RootObject);
 
 
-            Global.FastDefineDataDescriptor("Object", Object);
-            Global.FastDefineDataDescriptor("Function", Function);
-            Global.FastDefineDataDescriptor("Array", Array);
-            Global.FastDefineDataDescriptor("String", String);
-            Global.FastDefineDataDescriptor("Number", Number);
-            Global.FastDefineDataDescriptor("Boolean", Boolean);
-            Global.FastDefineDataDescriptor("Date", Date);
-            Global.FastDefineDataDescriptor("Math", Math);
+            Global.FastAddProperty("Object", Object, true, false, true);
+            Global.FastAddProperty("Function", Function, true, false, true);
+            Global.FastAddProperty("Array", Array, true, false, true);
+            Global.FastAddProperty("String", String, true, false, true);
+            Global.FastAddProperty("Number", Number, true, false, true);
+            Global.FastAddProperty("Boolean", Boolean, true, false, true);
+            Global.FastAddProperty("Date", Date, true, false, true);
+            Global.FastAddProperty("Math", Math, true, false, true);
 
 
             // create the global environment http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.3
             // create the global environment http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.3
             GlobalEnvironment = LexicalEnvironment.NewObjectEnvironment(Global, null, true);
             GlobalEnvironment = LexicalEnvironment.NewObjectEnvironment(Global, null, true);
@@ -75,12 +75,12 @@ namespace Jint
             {
             {
                 foreach (var entry in Options.GetDelegates())
                 foreach (var entry in Options.GetDelegates())
                 {
                 {
-                    Global.FastDefineDataDescriptor(entry.Key, new DelegateWrapper(this, entry.Value));
+                    Global.FastAddProperty(entry.Key, new DelegateWrapper(this, entry.Value), true, false, true);
                 }
                 }
             }
             }
 
 
             Eval = new EvalFunctionInstance(this, new ObjectInstance(null), new Identifier[0], LexicalEnvironment.NewDeclarativeEnvironment(ExecutionContext.LexicalEnvironment), Options.IsStrict());
             Eval = new EvalFunctionInstance(this, new ObjectInstance(null), new Identifier[0], LexicalEnvironment.NewDeclarativeEnvironment(ExecutionContext.LexicalEnvironment), Options.IsStrict());
-            Global.FastDefineDataDescriptor("eval", Eval);
+            Global.FastAddProperty("eval", Eval, true, false, true);
 
 
             _statements = new StatementInterpreter(this);
             _statements = new StatementInterpreter(this);
             _expressions = new ExpressionInterpreter(this);
             _expressions = new ExpressionInterpreter(this);
@@ -164,7 +164,7 @@ namespace Jint
                     return _statements.ExecuteForStatement(statement.As<ForStatement>());
                     return _statements.ExecuteForStatement(statement.As<ForStatement>());
                     
                     
                 case SyntaxNodes.ForInStatement:
                 case SyntaxNodes.ForInStatement:
-                    return _statements.ForInStatement(statement.As<ForInStatement>());
+                    return _statements.ExecuteForInStatement(statement.As<ForInStatement>());
 
 
                 case SyntaxNodes.FunctionDeclaration:
                 case SyntaxNodes.FunctionDeclaration:
                     return new Completion(Completion.Normal, null, null);
                     return new Completion(Completion.Normal, null, null);
@@ -314,36 +314,78 @@ namespace Jint
                     throw new ReferenceError();
                     throw new ReferenceError();
                 }
                 }
 
 
-                Global.Set(reference.GetReferencedName(), value);
+                Global.Put(reference.GetReferencedName(), value, false);
+            }
+            else if (reference.IsPropertyReference())
+            {
+                var baseValue = reference.GetBase();
+                if (!reference.HasPrimitiveBase())
+                {
+                    ((ObjectInstance)baseValue).Put(reference.GetReferencedName(), value, reference.IsStrict());
+                }
+                else
+                {
+                    PutPrimitiveBase(baseValue, reference.GetReferencedName(), value, reference.IsStrict());
+                }
             }
             }
-            //else if (reference.IsPropertyReference())
-            //{
-            //    if (!reference.HasPrimitiveBase())
-            //    {
-            //        // todo: complete implementation
-            //        throw new NotImplementedException();
-            //    }
-            //    else
-            //    {
-            //        // todo: complete implementation
-            //        throw new NotImplementedException();
-            //    }
-            //} 
             else
             else
             {
             {
                 var baseValue = reference.GetBase();
                 var baseValue = reference.GetBase();
                 var record = baseValue as EnvironmentRecord;
                 var record = baseValue as EnvironmentRecord;
-                
-                if (record != null)
+
+                record.SetMutableBinding(reference.GetReferencedName(), value, reference.IsStrict());
+            }
+        }
+
+        /// <summary>
+        /// Used by PutValue when the reference has a primitive base value
+        /// </summary>
+        /// <param name="b"></param>
+        /// <param name="name"></param>
+        /// <param name="value"></param>
+        /// <param name="throwOnError"></param>
+        public void PutPrimitiveBase(object b, string name, object value, bool throwOnError)
+        {
+            var o = TypeConverter.ToObject(this, b);
+            if (!o.CanPut(name))
+            {
+                if (throwOnError)
                 {
                 {
-                    record.SetMutableBinding(reference.GetReferencedName(), value, reference.IsStrict());
-                    return;
+                    throw new TypeError();
+                }
+
+                return;
+            }
+
+            var ownDesc = o.GetOwnProperty(name);
+
+            if (ownDesc.IsDataDescriptor())
+            {
+                if (throwOnError)
+                {
+                    throw new TypeError();
+                }
+
+                return;
+            }
+
+            var desc = o.GetProperty(name);
+
+            if (desc.IsAccessorDescriptor())
+            {
+                var setter = desc.As<AccessorDescriptor>().Set;
+                setter.Call(b, new[] { value });
+            }
+            else
+            {
+                if (throwOnError)
+                {
+                    throw new TypeError();
                 }
                 }
-                
-                ((ObjectInstance)baseValue).Set(reference.GetReferencedName(), value);
             }
             }
         }
         }
 
 
+
         public object GetGlobalValue(string propertyName)
         public object GetGlobalValue(string propertyName)
         {
         {
             if (System.String.IsNullOrEmpty(propertyName))
             if (System.String.IsNullOrEmpty(propertyName))

+ 1 - 1
Jint/Native/Object/ObjectConstructor.cs

@@ -27,7 +27,7 @@ namespace Jint.Native.Object
 
 
             // the constructor is the function constructor of an object
             // the constructor is the function constructor of an object
             instance.DefineOwnProperty("constructor", new DataDescriptor(this) { Writable = true, Enumerable = false, Configurable = false }, false);
             instance.DefineOwnProperty("constructor", new DataDescriptor(this) { Writable = true, Enumerable = false, Configurable = false }, false);
-            instance.DefineOwnProperty("prototype", new DataDescriptor(this.Prototype) { Writable = true, Enumerable = false, Configurable = false }, false);
+            instance.DefineOwnProperty("prototype", new DataDescriptor(this.Prototype) { Writable = false, Enumerable = false, Configurable = false }, false);
             return instance;
             return instance;
         }
         }
 
 

+ 39 - 6
Jint/Native/Object/ObjectInstance.cs

@@ -86,11 +86,12 @@ namespace Jint.Native.Object
         /// </summary>
         /// </summary>
         /// <param name="propertyName"></param>
         /// <param name="propertyName"></param>
         /// <returns></returns>
         /// <returns></returns>
-        public PropertyDescriptor GetOwnProperty(string propertyName)
+        public virtual PropertyDescriptor GetOwnProperty(string propertyName)
         {
         {
             PropertyDescriptor x;
             PropertyDescriptor x;
             if (Properties.TryGetValue(propertyName, out x))
             if (Properties.TryGetValue(propertyName, out x))
             {
             {
+                /* Spec implementation
                 PropertyDescriptor d;
                 PropertyDescriptor d;
                 if (x.IsDataDescriptor())
                 if (x.IsDataDescriptor())
                 {
                 {
@@ -102,6 +103,10 @@ namespace Jint.Native.Object
                 }
                 }
 
 
                 return d;
                 return d;
+                */
+
+                // optimmized implementation
+                return x;
             }
             }
             
             
             return PropertyDescriptor.Undefined;
             return PropertyDescriptor.Undefined;
@@ -153,11 +158,27 @@ namespace Jint.Native.Object
 
 
             if (ownDesc.IsDataDescriptor())
             if (ownDesc.IsDataDescriptor())
             {
             {
-                var valueDesc = new DataDescriptor(value);
-                DefineOwnProperty(propertyName, valueDesc, throwOnError);
+                /* Spec implementation 
+                 * var valueDesc = new DataDescriptor(value);
+                 * DefineOwnProperty(propertyName, valueDesc, throwOnError);
+                 */
+
+                // optimized implementation which doesn't require to create a new descriptor
+                if (!ownDesc.Configurable)
+                {
+                    if (throwOnError)
+                    {
+                        throw new TypeError();
+                    }
+
+                    return;
+                }
+
+                ownDesc.As<DataDescriptor>().Value = value;
                 return;
                 return;
             }
             }
 
 
+            // property is an accessor or inherited
             var desc = GetProperty(propertyName);
             var desc = GetProperty(propertyName);
 
 
             if (desc.IsAccessorDescriptor())
             if (desc.IsAccessorDescriptor())
@@ -471,14 +492,26 @@ namespace Jint.Native.Object
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Optimized version of [[Set]] when the property is known to be undeclared already
+        /// Optimized version of [[Put]] when the property is known to be undeclared already
         /// </summary>
         /// </summary>
         /// <param name="name"></param>
         /// <param name="name"></param>
         /// <param name="value"></param>
         /// <param name="value"></param>
-        public void FastDefineDataDescriptor(string name, object value)
+        /// <param name="writable"></param>
+        /// <param name="configurable"></param>
+        /// <param name="enumerable"></param>
+        public void FastAddProperty(string name, object value, bool writable, bool enumerable, bool configurable)
         {
         {
-            Properties.Add(name, new DataDescriptor(value) { Configurable = true, Enumerable = true, Writable = true });
+            Properties.Add(name, new DataDescriptor(value) { Writable = writable, Enumerable = enumerable, Configurable = configurable });
         }
         }
 
 
+        /// <summary>
+        /// Optimized version of [[Put]] when the property is known to be already declared 
+        /// </summary>
+        /// <param name="name"></param>
+        /// <param name="value"></param>
+        public void FastSetProperty(string name, PropertyDescriptor value)
+        {
+            Properties[name] = value;
+        }
     }
     }
 }
 }

+ 7 - 0
Jint/Native/String/StringInstance.cs

@@ -29,5 +29,12 @@ namespace Jint.Native.String
         }
         }
 
 
         public string PrimitiveValue { get; set; }
         public string PrimitiveValue { get; set; }
+
+        public override Runtime.Descriptors.PropertyDescriptor GetOwnProperty(string propertyName)
+        {
+            // todo: http://www.ecma-international.org/ecma-262/5.1/#sec-15.5.5.2
+
+            return base.GetOwnProperty(propertyName);
+        }
     }
     }
 }
 }

+ 2 - 2
Jint/Runtime/Descriptors/PropertyDescriptor.cs

@@ -57,12 +57,12 @@
         {
         {
             public override bool IsAccessorDescriptor()
             public override bool IsAccessorDescriptor()
             {
             {
-                throw new System.NotImplementedException();
+                return false;
             }
             }
 
 
             public override bool IsDataDescriptor()
             public override bool IsDataDescriptor()
             {
             {
-                throw new System.NotImplementedException();
+                return false;
             }
             }
         }
         }
 
 

+ 7 - 5
Jint/Runtime/References/Reference.cs

@@ -1,6 +1,9 @@
 using Jint.Native;
 using Jint.Native;
+using Jint.Native.Boolean;
 using Jint.Native.Errors;
 using Jint.Native.Errors;
+using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Native.Object;
+using Jint.Native.String;
 using Jint.Runtime.Environments;
 using Jint.Runtime.Environments;
 
 
 namespace Jint.Runtime.References
 namespace Jint.Runtime.References
@@ -43,10 +46,9 @@ namespace Jint.Runtime.References
 
 
         public bool HasPrimitiveBase()
         public bool HasPrimitiveBase()
         {
         {
-            return false;
-                // (_baseValue is BooleanInstance)
-                // || (_baseValue is StringInstance)
-                // || (_baseValue is NumberInstance)
+            return (_baseValue is BooleanInstance)
+                || (_baseValue is StringInstance)
+                || (_baseValue is NumberInstance)
                 ;
                 ;
         }
         }
 
 
@@ -57,7 +59,7 @@ namespace Jint.Runtime.References
 
 
         public bool IsPropertyReference()
         public bool IsPropertyReference()
         {
         {
-            /// todo: complete implementation  http://www.ecma-international.org/ecma-262/5.1/#sec-8.7
+            // http://www.ecma-international.org/ecma-262/5.1/#sec-8.7
             return _baseValue is ObjectInstance || HasPrimitiveBase();
             return _baseValue is ObjectInstance || HasPrimitiveBase();
         }
         }
     }
     }

+ 10 - 16
Jint/Runtime/StatementInterpreter.cs

@@ -191,12 +191,12 @@ namespace Jint.Runtime
         /// </summary>
         /// </summary>
         /// <param name="forInStatement"></param>
         /// <param name="forInStatement"></param>
         /// <returns></returns>
         /// <returns></returns>
-        public Completion ForInStatement(ForInStatement forInStatement)
+        public Completion ExecuteForInStatement(ForInStatement forInStatement)
         {
         {
             object varName = null;
             object varName = null;
             if (forInStatement.Left.Type == SyntaxNodes.VariableDeclaration)
             if (forInStatement.Left.Type == SyntaxNodes.VariableDeclaration)
             {
             {
-                varName = ExecuteStatement(forInStatement.Left.As<Statement>()).Value;
+                varName = ExecuteVariableDeclaration(forInStatement.Left.As<VariableDeclaration>()).Value;
             }
             }
             
             
             var exprRef = _engine.EvaluateExpression(forInStatement.Right);
             var exprRef = _engine.EvaluateExpression(forInStatement.Right);
@@ -205,6 +205,11 @@ namespace Jint.Runtime
             {
             {
                 return new Completion(Completion.Normal, null, null);
                 return new Completion(Completion.Normal, null, null);
             }
             }
+
+            Reference leftRef = varName != null
+                                    ? varName as Reference
+                                    : _engine.EvaluateExpression(forInStatement.Left.As<Expression>()) as Reference;
+
             var obj = TypeConverter.ToObject(_engine, experValue);
             var obj = TypeConverter.ToObject(_engine, experValue);
             object v = null;
             object v = null;
             foreach (var entry in obj.Properties)
             foreach (var entry in obj.Properties)
@@ -215,17 +220,8 @@ namespace Jint.Runtime
                 }
                 }
 
 
                 var p = entry.Key;
                 var p = entry.Key;
+                _engine.PutValue(leftRef, p);
 
 
-                if (varName != null)
-                {
-                    var varRef = varName as Reference;
-                    _engine.PutValue(varRef, p);
-                }
-                else
-                {
-                    var lhsRef = _engine.EvaluateExpression(forInStatement.Left.As<Expression>()) as Reference;
-                    _engine.PutValue(lhsRef, p);
-                }
                 var stmt = ExecuteStatement(forInStatement.Body);
                 var stmt = ExecuteStatement(forInStatement.Body);
                 if (stmt.Value != null)
                 if (stmt.Value != null)
                 {
                 {
@@ -461,18 +457,16 @@ namespace Jint.Runtime
 
 
         public Completion ExecuteVariableDeclaration(VariableDeclaration statement)
         public Completion ExecuteVariableDeclaration(VariableDeclaration statement)
         {
         {
-            var env = _engine.ExecutionContext.LexicalEnvironment.Record;
             object value = Undefined.Instance;
             object value = Undefined.Instance;
 
 
             foreach (var declaration in statement.Declarations)
             foreach (var declaration in statement.Declarations)
             {
             {
-                var dn = declaration.Id.Name;
                 if (declaration.Init != null)
                 if (declaration.Init != null)
                 {
                 {
+                    var lhs = _engine.EvaluateExpression(declaration.Id) as Reference;
                     value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init));
                     value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init));
+                    _engine.PutValue(lhs, value);
                 }
                 }
-
-                env.SetMutableBinding(dn, value, false);
             }
             }
 
 
             return new Completion(Completion.Normal, value, null);
             return new Completion(Completion.Normal, value, null);