|
@@ -1,4 +1,5 @@
|
|
-using System.Linq;
|
|
|
|
|
|
+using System.Collections.Generic;
|
|
|
|
+using System.Linq;
|
|
using Jint.Native.Object;
|
|
using Jint.Native.Object;
|
|
using Jint.Runtime;
|
|
using Jint.Runtime;
|
|
using Jint.Runtime.Descriptors;
|
|
using Jint.Runtime.Descriptors;
|
|
@@ -8,7 +9,9 @@ namespace Jint.Native.Array
|
|
public class ArrayInstance : ObjectInstance
|
|
public class ArrayInstance : ObjectInstance
|
|
{
|
|
{
|
|
private readonly Engine _engine;
|
|
private readonly Engine _engine;
|
|
-
|
|
|
|
|
|
+ private IDictionary<uint, PropertyDescriptor> _array = new MruPropertyCache2<uint, PropertyDescriptor>();
|
|
|
|
+ private PropertyDescriptor _length;
|
|
|
|
+
|
|
public ArrayInstance(Engine engine) : base(engine)
|
|
public ArrayInstance(Engine engine) : base(engine)
|
|
{
|
|
{
|
|
_engine = engine;
|
|
_engine = engine;
|
|
@@ -64,7 +67,9 @@ namespace Jint.Native.Array
|
|
public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
|
|
public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
|
|
{
|
|
{
|
|
var oldLenDesc = GetOwnProperty("length");
|
|
var oldLenDesc = GetOwnProperty("length");
|
|
- var oldLen = TypeConverter.ToNumber(oldLenDesc.Value.Value);
|
|
|
|
|
|
+ var oldLen = (uint)TypeConverter.ToNumber(oldLenDesc.Value.Value);
|
|
|
|
+ uint index;
|
|
|
|
+
|
|
if (propertyName == "length")
|
|
if (propertyName == "length")
|
|
{
|
|
{
|
|
if (!desc.Value.HasValue)
|
|
if (!desc.Value.HasValue)
|
|
@@ -82,7 +87,7 @@ namespace Jint.Native.Array
|
|
newLenDesc.Value = newLen;
|
|
newLenDesc.Value = newLen;
|
|
if (newLen >= oldLen)
|
|
if (newLen >= oldLen)
|
|
{
|
|
{
|
|
- return base.DefineOwnProperty("length", newLenDesc, throwOnError);
|
|
|
|
|
|
+ return base.DefineOwnProperty("length", _length = newLenDesc, throwOnError);
|
|
}
|
|
}
|
|
if (!oldLenDesc.Writable.Value)
|
|
if (!oldLenDesc.Writable.Value)
|
|
{
|
|
{
|
|
@@ -103,33 +108,35 @@ namespace Jint.Native.Array
|
|
newWritable = false;
|
|
newWritable = false;
|
|
newLenDesc.Writable = true;
|
|
newLenDesc.Writable = true;
|
|
}
|
|
}
|
|
-
|
|
|
|
- var succeeded = base.DefineOwnProperty("length", newLenDesc, throwOnError);
|
|
|
|
|
|
+
|
|
|
|
+ var succeeded = base.DefineOwnProperty("length", _length = newLenDesc, throwOnError);
|
|
if (!succeeded)
|
|
if (!succeeded)
|
|
{
|
|
{
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
+
|
|
// in the case of sparse arrays, treat each concrete element instead of
|
|
// in the case of sparse arrays, treat each concrete element instead of
|
|
// iterating over all indexes
|
|
// iterating over all indexes
|
|
|
|
|
|
- if (Properties.Count < oldLen - newLen)
|
|
|
|
|
|
+ if (_array.Count < oldLen - newLen)
|
|
{
|
|
{
|
|
- var keys = Properties.Keys.ToArray();
|
|
|
|
|
|
+ var keys = _array.Keys.ToArray();
|
|
foreach (var key in keys)
|
|
foreach (var key in keys)
|
|
{
|
|
{
|
|
- uint index;
|
|
|
|
|
|
+ uint keyIndex;
|
|
// is it the index of the array
|
|
// is it the index of the array
|
|
- if (uint.TryParse(key, out index) && index >= newLen && index < oldLen)
|
|
|
|
|
|
+ if (IsArrayIndex(key, out keyIndex) && keyIndex >= newLen && keyIndex < oldLen)
|
|
{
|
|
{
|
|
- var deleteSucceeded = Delete(key, false);
|
|
|
|
|
|
+ var deleteSucceeded = Delete(key.ToString(), false);
|
|
if (!deleteSucceeded)
|
|
if (!deleteSucceeded)
|
|
{
|
|
{
|
|
- newLenDesc.Value = new JsValue(index + 1);
|
|
|
|
|
|
+ newLenDesc.Value = new JsValue(keyIndex + 1);
|
|
if (!newWritable)
|
|
if (!newWritable)
|
|
{
|
|
{
|
|
newLenDesc.Writable = false;
|
|
newLenDesc.Writable = false;
|
|
}
|
|
}
|
|
- base.DefineOwnProperty("length", newLenDesc, false);
|
|
|
|
|
|
+ base.DefineOwnProperty("length", _length = newLenDesc, false);
|
|
|
|
+
|
|
if (throwOnError)
|
|
if (throwOnError)
|
|
{
|
|
{
|
|
throw new JavaScriptException(_engine.TypeError);
|
|
throw new JavaScriptException(_engine.TypeError);
|
|
@@ -154,7 +161,8 @@ namespace Jint.Native.Array
|
|
{
|
|
{
|
|
newLenDesc.Writable = false;
|
|
newLenDesc.Writable = false;
|
|
}
|
|
}
|
|
- base.DefineOwnProperty("length", newLenDesc, false);
|
|
|
|
|
|
+ base.DefineOwnProperty("length", _length = newLenDesc, false);
|
|
|
|
+
|
|
if (throwOnError)
|
|
if (throwOnError)
|
|
{
|
|
{
|
|
throw new JavaScriptException(_engine.TypeError);
|
|
throw new JavaScriptException(_engine.TypeError);
|
|
@@ -170,9 +178,8 @@ namespace Jint.Native.Array
|
|
}
|
|
}
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
- else if (IsArrayIndex(propertyName))
|
|
|
|
|
|
+ else if (IsArrayIndex(propertyName, out index))
|
|
{
|
|
{
|
|
- var index = TypeConverter.ToUint32(propertyName);
|
|
|
|
if (index >= oldLen && !oldLenDesc.Writable.Value)
|
|
if (index >= oldLen && !oldLenDesc.Writable.Value)
|
|
{
|
|
{
|
|
if (throwOnError)
|
|
if (throwOnError)
|
|
@@ -195,7 +202,7 @@ namespace Jint.Native.Array
|
|
if (index >= oldLen)
|
|
if (index >= oldLen)
|
|
{
|
|
{
|
|
oldLenDesc.Value = index + 1;
|
|
oldLenDesc.Value = index + 1;
|
|
- base.DefineOwnProperty("length", oldLenDesc, false);
|
|
|
|
|
|
+ base.DefineOwnProperty("length", _length = oldLenDesc, false);
|
|
}
|
|
}
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
@@ -203,10 +210,122 @@ namespace Jint.Native.Array
|
|
return base.DefineOwnProperty(propertyName, desc, throwOnError);
|
|
return base.DefineOwnProperty(propertyName, desc, throwOnError);
|
|
}
|
|
}
|
|
|
|
|
|
- public static bool IsArrayIndex(JsValue p)
|
|
|
|
|
|
+ private uint GetLength()
|
|
|
|
+ {
|
|
|
|
+ return TypeConverter.ToUint32(_length.Value.Value);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
|
|
|
|
+ {
|
|
|
|
+ foreach(var entry in _array)
|
|
|
|
+ {
|
|
|
|
+ yield return new KeyValuePair<string, PropertyDescriptor>(entry.Key.ToString(), entry.Value);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ foreach(var entry in base.GetOwnProperties())
|
|
|
|
+ {
|
|
|
|
+ yield return entry;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public override PropertyDescriptor GetOwnProperty(string propertyName)
|
|
{
|
|
{
|
|
- return TypeConverter.ToString(TypeConverter.ToUint32(p)) == TypeConverter.ToString(p) && TypeConverter.ToUint32(p) != uint.MaxValue;
|
|
|
|
|
|
+ uint index;
|
|
|
|
+ if (IsArrayIndex(propertyName, out index))
|
|
|
|
+ {
|
|
|
|
+ PropertyDescriptor result;
|
|
|
|
+ if (_array.TryGetValue(index, out result))
|
|
|
|
+ {
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ return PropertyDescriptor.Undefined;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return base.GetOwnProperty(propertyName);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ protected override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
|
|
|
|
+ {
|
|
|
|
+ uint index;
|
|
|
|
+ if (IsArrayIndex(propertyName, out index))
|
|
|
|
+ {
|
|
|
|
+ _array[index] = desc;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ if(propertyName == "length")
|
|
|
|
+ {
|
|
|
|
+ _length = desc;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ base.SetOwnProperty(propertyName, desc);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ public override bool HasOwnProperty(string p)
|
|
|
|
+ {
|
|
|
|
+ uint index;
|
|
|
|
+ if (IsArrayIndex(p, out index))
|
|
|
|
+ {
|
|
|
|
+ return index < GetLength() && _array.ContainsKey(index);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return base.HasOwnProperty(p);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public override void RemoveOwnProperty(string p)
|
|
|
|
+ {
|
|
|
|
+ uint index;
|
|
|
|
+ if(IsArrayIndex(p, out index))
|
|
|
|
+ {
|
|
|
|
+ _array.Remove(index);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ base.RemoveOwnProperty(p);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static bool IsArrayIndex(JsValue p, out uint index)
|
|
|
|
+ {
|
|
|
|
+ index = ParseArrayIndex(TypeConverter.ToString(p));
|
|
|
|
+
|
|
|
|
+ return index != uint.MaxValue;
|
|
|
|
+
|
|
|
|
+ // 15.4 - Use an optimized version of the specification
|
|
|
|
+ // return TypeConverter.ToString(index) == TypeConverter.ToString(p) && index != uint.MaxValue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ internal static uint ParseArrayIndex(string p)
|
|
|
|
+ {
|
|
|
|
+ int d = p[0] - '0';
|
|
|
|
+
|
|
|
|
+ if (d < 0 || d > 9)
|
|
|
|
+ {
|
|
|
|
+ return uint.MaxValue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ulong result = (uint)d;
|
|
|
|
+
|
|
|
|
+ for (int i = 1; i < p.Length; i++)
|
|
|
|
+ {
|
|
|
|
+ d = p[i] - '0';
|
|
|
|
+
|
|
|
|
+ if (d < 0 || d > 9)
|
|
|
|
+ {
|
|
|
|
+ return uint.MaxValue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ result = result * 10 + (uint)d;
|
|
|
|
+
|
|
|
|
+ if (result >= uint.MaxValue)
|
|
|
|
+ {
|
|
|
|
+ return uint.MaxValue;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return (uint)result;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|