Browse Source

Implementing Array indexer

Sebastien Ros 12 years ago
parent
commit
b565752107

+ 0 - 5
Jint.Repl/Program.cs

@@ -1,10 +1,5 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using Jint.Native;
-using Jint.Native.Json;
 using Jint.Runtime;
 
 namespace Jint.Repl

+ 34 - 14
Jint/Native/Array/ArrayConstructor.cs

@@ -1,5 +1,7 @@
-using Jint.Native.Function;
+using System.Linq;
+using Jint.Native.Function;
 using Jint.Native.Object;
+using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
 
@@ -17,9 +19,6 @@ namespace Jint.Native.Array
             Prototype.DefineOwnProperty("constructor", new DataDescriptor(this) { Writable = true, Enumerable = false, Configurable = false }, false);
             Prototype.DefineOwnProperty("prototype", new DataDescriptor(Prototype) { Writable = true, Enumerable = false, Configurable = false }, false);
                                   
-            // Array prototype properties
-            Prototype.DefineOwnProperty("length", new ClrAccessDescriptor<ArrayInstance>(_engine, x => x.Length), false);
-
             // Array prototype functions
             Prototype.DefineOwnProperty("push", new ClrDataDescriptor<ArrayInstance, object>(engine, Push), false);
             Prototype.DefineOwnProperty("pop", new ClrDataDescriptor<ArrayInstance, object>(engine, Pop), false);
@@ -33,24 +32,45 @@ namespace Jint.Native.Array
         public ObjectInstance Construct(object[] arguments)
         {
             var instance = new ArrayInstance(_engine, Prototype);
-
-            foreach (var arg in arguments)
-            {
-                instance.Push(arg);
-            }
+            instance.FastAddProperty("length", 0, true, false, true);
+            Push(instance, arguments);
 
             return instance;
         }
 
-        private static object Push(ArrayInstance thisObject, object[] arguments)
+        private object Push(object thisObject, object[] arguments)
         {
-            thisObject.Push(arguments[0]);
-            return arguments[0];
+            var o = TypeConverter.ToObject(_engine, thisObject);
+            var lenVal = o.Get("length");
+            var n = TypeConverter.ToUint32(lenVal);
+            foreach (var e in arguments)
+            {
+                o.Put(TypeConverter.ToString(n), e, true);
+                n++;
+            }
+            o.Put("length", n, true);
+
+            return n;
         }
 
-        private static object Pop(ArrayInstance thisObject, object[] arguments)
+        private object Pop(object thisObject, object[] arguments)
         {
-            return thisObject.Pop();
+            var o = TypeConverter.ToObject(_engine, thisObject);
+            var lenVal = o.Get("length");
+            var len = TypeConverter.ToUint32(lenVal);
+            if (len == 0)
+            {
+                o.Put("length", 0, true);
+                return Undefined.Instance;
+            }
+            else
+            {
+                var indx = TypeConverter.ToString(len - 1);
+                var element = o.Get(indx);
+                o.Delete(indx, true);
+                o.Put("length", indx, true);
+                return element;
+            }
         }
     }
 }

+ 106 - 7
Jint/Native/Array/ArrayInstance.cs

@@ -1,14 +1,17 @@
 using System.Collections.Generic;
 using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
 
 namespace Jint.Native.Array
 {
     public sealed class ArrayInstance : ObjectInstance
     {
-        private readonly Stack<object> _array = new Stack<object>();
+        private readonly Engine _engine;
  
         public ArrayInstance(Engine engine, ObjectInstance prototype) : base(engine, prototype)
         {
+            _engine = engine;
         }
 
         public override string Class
@@ -19,16 +22,112 @@ namespace Jint.Native.Array
             }
         }
 
-        public double Length { get { return _array.Count; } }
-
-        public void Push(object o)
+        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         {
-            _array.Push(o);
+            var oldLenDesc = GetOwnProperty("length").As<DataDescriptor>();
+            var oldLen = TypeConverter.ToNumber(oldLenDesc.Value);
+            if (propertyName == "length")
+            {
+                var descData = desc as DataDescriptor;
+                if (descData == null)
+                {
+                    return base.DefineOwnProperty("length", desc, throwOnError);
+                }
+                
+                var newLenDesc = new DataDescriptor(desc);
+                double newLen = TypeConverter.ToUint32(descData.Value);
+                if (newLen != TypeConverter.ToNumber(descData.Value))
+                {
+                    throw new JavaScriptException(_engine.RangeError);
+                }
+                newLenDesc.Value = newLen;
+                if (newLen >= oldLen)
+                {
+                    return base.DefineOwnProperty("length", newLenDesc, throwOnError);
+                }
+                if (!oldLenDesc.Writable)
+                {
+                    if (throwOnError)
+                    {
+                        throw new JavaScriptException(_engine.TypeError);
+                    }
+
+                    return false;
+                }
+                var newWritable = true;
+                if (!newLenDesc.Writable)
+                {
+                    newWritable = false;
+                    newLenDesc.Writable = true;
+                }
+                var succeeded = base.DefineOwnProperty("length", newLenDesc, throwOnError);
+                if (!succeeded)
+                {
+                    return false;
+                }
+                while (newLen < oldLen)
+                {
+                    oldLen--;
+                    var deleteSucceeded = Delete(TypeConverter.ToString(oldLen), false);
+                    if (!deleteSucceeded)
+                    {
+                        newLenDesc.Value = oldLen + 1;
+                        if (!newWritable)
+                        {
+                            newLenDesc.Writable = false;
+                        }
+                        base.DefineOwnProperty("length", newLenDesc, false);
+                        if (throwOnError)
+                        {
+                            throw new JavaScriptException(_engine.TypeError);
+                        }
+
+                        return false;
+                    }
+                }
+                if (!newWritable)
+                {
+                    DefineOwnProperty("length", new DataDescriptor(null) {Writable = false}, false);
+                }
+                return true;
+            }
+            else if (IsArrayIndex(propertyName))
+            {
+                var index = TypeConverter.ToUint32(propertyName);
+                if (index >= oldLen && !oldLenDesc.Writable)
+                {
+                    if (throwOnError)
+                    {
+                        throw new JavaScriptException(_engine.TypeError);
+                    }
+
+                    return false;
+                }
+                var succeeded = base.DefineOwnProperty(propertyName, desc, false);
+                if (!succeeded)
+                {
+                    if (throwOnError)
+                    {
+                        throw new JavaScriptException(_engine.TypeError);
+                    }
+
+                    return false;
+                }
+                if (index >= oldLen)
+                {
+                    oldLenDesc.Value = index + 1;
+                    base.DefineOwnProperty("length", oldLenDesc, false);
+                }
+                return true;
+            }
+
+            return base.DefineOwnProperty(propertyName, desc, false);
         }
 
-        public object Pop()
+        public static bool IsArrayIndex(object p)
         {
-            return _array.Pop();
+            return TypeConverter.ToString(TypeConverter.ToUint32(p)) == TypeConverter.ToString(p) && TypeConverter.ToUint32(p) != uint.MaxValue;
         }
+
     }
 }

+ 2 - 1
Jint/Native/Json/JsonSerializer.cs

@@ -245,7 +245,8 @@ namespace Jint.Native.Json
             var stepback = _indent;
             _indent = _indent + _gap;
             var partial = new List<string>();
-            for (int i = 0; i < value.Length; i++)
+            var len = TypeConverter.ToUint32(value.Get("length"));
+            for (int i = 0; i < len; i++)
             {
                 var strP = Str(TypeConverter.ToString(i), value);
                 partial.Add(strP);

+ 2 - 2
Jint/Native/Object/ObjectInstance.cs

@@ -16,7 +16,7 @@ namespace Jint.Native.Object
             Properties = new Dictionary<string, PropertyDescriptor>();
             Extensible = true;
             Prototype = prototype;
-            DefineOwnProperty("prototype", new DataDescriptor(prototype), false);
+            FastAddProperty("prototype", prototype, false, false, false);
         }
 
         public IDictionary<string, PropertyDescriptor> Properties { get; private set; }
@@ -373,7 +373,7 @@ namespace Jint.Native.Object
         /// <param name="desc"></param>
         /// <param name="throwOnError"></param>
         /// <returns></returns>
-        public bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
+        public virtual bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             var current = GetOwnProperty(propertyName);
             

+ 88 - 2
Jint/Runtime/ExpressionIntepreter.cs

@@ -197,11 +197,11 @@ namespace Jint.Runtime
                     break;
 
                 case "==":
-                    value = left.Equals(right);
+                    value = Equal(left, right);
                     break;
                 
                 case "!=":
-                    value = !left.Equals(right);
+                    value = !Equal(left, right);
                     break;
                 
                 case ">":
@@ -284,6 +284,92 @@ namespace Jint.Runtime
             }
         }
 
+        public static bool Equal(object x, object y)
+        {
+            var typex = TypeConverter.GetType(x);
+            var typey = TypeConverter.GetType(y);
+
+            if (typex == typey)
+            {
+                if (typex == TypeCode.Empty)
+                {
+                    return true;
+                }
+
+                if (typex == TypeCode.Double)
+                {
+                    var nx = TypeConverter.ToNumber(x);
+                    var ny = TypeConverter.ToNumber(y);
+
+                    if (double.IsNaN(nx) || double.IsNaN(ny))
+                    {
+                        return false;
+                    }
+
+                    if (nx == ny)
+                    {
+                        return true;
+                    }
+
+                    return false;
+                }
+
+                if (typex == TypeCode.String)
+                {
+                    return TypeConverter.ToString(x) == TypeConverter.ToString(y);
+                }
+
+                if (typex == TypeCode.Boolean)
+                {
+                    return (bool) x == (bool) y;
+                }
+
+                return x == y;
+            }
+
+            if (x == Null.Instance && y == Undefined.Instance)
+            {
+                return true;
+            }
+
+            if (x == Undefined.Instance && y == Null.Instance)
+            {
+                return true;
+            }
+
+            if (typex == TypeCode.Double && typey == TypeCode.String)
+            {
+                return Equal(x, TypeConverter.ToNumber(y));
+            }
+
+            if (typex == TypeCode.String && typey == TypeCode.Double)
+            {
+                return Equal(TypeConverter.ToNumber(x), y);
+            }
+
+            if (typex == TypeCode.Boolean)
+            {
+                return Equal(TypeConverter.ToNumber(x), y);
+            }
+
+            if (typey == TypeCode.Boolean)
+            {
+                return Equal(x, TypeConverter.ToNumber(y));
+            }
+
+            if (typey == TypeCode.Object && (typex == TypeCode.String || typex == TypeCode.Double))
+            {
+                return Equal(x, TypeConverter.ToPrimitive(y));
+            }
+
+            if (typex == TypeCode.Object && (typey == TypeCode.String || typey == TypeCode.Double))
+            {
+                return Equal(TypeConverter.ToPrimitive(x), y);
+            }
+
+            return false;
+        }
+        
         public static bool StriclyEqual(object x, object y)
         {
             var typea = TypeConverter.GetType(x);