ArrayInstance.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. using System.Linq;
  2. using Jint.Native.Object;
  3. using Jint.Runtime;
  4. using Jint.Runtime.Descriptors;
  5. namespace Jint.Native.Array
  6. {
  7. public class ArrayInstance : ObjectInstance
  8. {
  9. private readonly Engine _engine;
  10. public ArrayInstance(Engine engine) : base(engine)
  11. {
  12. _engine = engine;
  13. }
  14. public override string Class
  15. {
  16. get
  17. {
  18. return "Array";
  19. }
  20. }
  21. public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
  22. {
  23. var oldLenDesc = GetOwnProperty("length");
  24. var oldLen = TypeConverter.ToNumber(oldLenDesc.Value.Value);
  25. if (propertyName == "length")
  26. {
  27. if (desc.Value.IsAbsent)
  28. {
  29. return base.DefineOwnProperty("length", desc, throwOnError);
  30. }
  31. var newLenDesc = new PropertyDescriptor(desc);
  32. uint newLen = TypeConverter.ToUint32(desc.Value.Value);
  33. if (newLen != TypeConverter.ToNumber(desc.Value.Value))
  34. {
  35. throw new JavaScriptException(_engine.RangeError);
  36. }
  37. newLenDesc.Value = new Field<object>(newLen);
  38. if (newLen >= oldLen)
  39. {
  40. return base.DefineOwnProperty("length", newLenDesc, throwOnError);
  41. }
  42. if (!oldLenDesc.Writable.Value)
  43. {
  44. if (throwOnError)
  45. {
  46. throw new JavaScriptException(_engine.TypeError);
  47. }
  48. return false;
  49. }
  50. bool newWritable;
  51. if (newLenDesc.Writable.IsAbsent || newLenDesc.Writable.Value)
  52. {
  53. newWritable = true;
  54. }
  55. else
  56. {
  57. newWritable = false;
  58. newLenDesc.Writable = new Field<bool>(true);
  59. }
  60. var succeeded = base.DefineOwnProperty("length", newLenDesc, throwOnError);
  61. if (!succeeded)
  62. {
  63. return false;
  64. }
  65. // in the case of sparse arrays, treat each concrete element instead of
  66. // iterating over all indexes
  67. if (Properties.Count < oldLen - newLen)
  68. {
  69. var keys = Properties.Keys.ToArray();
  70. foreach (var key in keys)
  71. {
  72. uint index;
  73. // is it the index of the array
  74. if (uint.TryParse(key, out index) && index >= newLen && index < oldLen)
  75. {
  76. var deleteSucceeded = Delete(key, false);
  77. if (!deleteSucceeded)
  78. {
  79. newLenDesc.Value = new Field<object>(index + 1);
  80. if (!newWritable)
  81. {
  82. newLenDesc.Writable = new Field<bool>(false);
  83. }
  84. base.DefineOwnProperty("length", newLenDesc, false);
  85. if (throwOnError)
  86. {
  87. throw new JavaScriptException(_engine.TypeError);
  88. }
  89. return false;
  90. }
  91. }
  92. }
  93. }
  94. else
  95. {
  96. while (newLen < oldLen)
  97. {
  98. // algorithm as per the spec
  99. oldLen--;
  100. var deleteSucceeded = Delete(TypeConverter.ToString(oldLen), false);
  101. if (!deleteSucceeded)
  102. {
  103. newLenDesc.Value = new Field<object>(oldLen + 1);
  104. if (!newWritable)
  105. {
  106. newLenDesc.Writable = new Field<bool>(false);
  107. }
  108. base.DefineOwnProperty("length", newLenDesc, false);
  109. if (throwOnError)
  110. {
  111. throw new JavaScriptException(_engine.TypeError);
  112. }
  113. return false;
  114. }
  115. }
  116. }
  117. if (!newWritable)
  118. {
  119. DefineOwnProperty("length", new PropertyDescriptor(value: null, writable: false, enumerable: null, configurable: null), false);
  120. }
  121. return true;
  122. }
  123. else if (IsArrayIndex(propertyName))
  124. {
  125. var index = TypeConverter.ToUint32(propertyName);
  126. if (index >= oldLen && !oldLenDesc.Writable.Value)
  127. {
  128. if (throwOnError)
  129. {
  130. throw new JavaScriptException(_engine.TypeError);
  131. }
  132. return false;
  133. }
  134. var succeeded = base.DefineOwnProperty(propertyName, desc, false);
  135. if (!succeeded)
  136. {
  137. if (throwOnError)
  138. {
  139. throw new JavaScriptException(_engine.TypeError);
  140. }
  141. return false;
  142. }
  143. if (index >= oldLen)
  144. {
  145. oldLenDesc.Value = new Field<object>(index + 1);
  146. base.DefineOwnProperty("length", oldLenDesc, false);
  147. }
  148. return true;
  149. }
  150. return base.DefineOwnProperty(propertyName, desc, throwOnError);
  151. }
  152. public static bool IsArrayIndex(object p)
  153. {
  154. return TypeConverter.ToString(TypeConverter.ToUint32(p)) == TypeConverter.ToString(p) && TypeConverter.ToUint32(p) != uint.MaxValue;
  155. }
  156. }
  157. }