ArrayOperations.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. using Jint.Native.Number;
  2. using Jint.Native.Object;
  3. using Jint.Runtime;
  4. using Jint.Runtime.Descriptors;
  5. namespace Jint.Native.Array
  6. {
  7. internal abstract class ArrayOperations
  8. {
  9. protected internal const ulong MaxArrayLength = 4294967295;
  10. protected internal const ulong MaxArrayLikeLength = NumberConstructor.MaxSafeInteger;
  11. public static ArrayOperations For(ObjectInstance instance)
  12. {
  13. if (instance is ArrayInstance arrayInstance)
  14. {
  15. return new ArrayInstanceOperations(arrayInstance);
  16. }
  17. return new ObjectInstanceOperations(instance);
  18. }
  19. public static ArrayOperations For(Engine engine, JsValue thisObj)
  20. {
  21. var instance = TypeConverter.ToObject(engine, thisObj);
  22. return For(instance);
  23. }
  24. public abstract ObjectInstance Target { get; }
  25. public abstract ulong GetSmallestIndex(ulong length);
  26. public abstract uint GetLength();
  27. public abstract ulong GetLongLength();
  28. public abstract void SetLength(ulong length);
  29. public abstract void EnsureCapacity(ulong capacity);
  30. public abstract JsValue Get(ulong index);
  31. public virtual JsValue[] GetAll(Types elementTypes)
  32. {
  33. var n = (int) GetLength();
  34. var jsValues = new JsValue[n];
  35. for (uint i = 0; i < (uint) jsValues.Length; i++)
  36. {
  37. var jsValue = Get(i);
  38. if ((jsValue.Type & elementTypes) == 0)
  39. {
  40. ExceptionHelper.ThrowTypeErrorNoEngine<object>("invalid type");
  41. }
  42. jsValues[i] = jsValue;
  43. }
  44. return jsValues;
  45. }
  46. public abstract bool TryGetValue(ulong index, out JsValue value);
  47. public bool HasProperty(ulong index) => Target.HasProperty(index);
  48. public abstract void Set(ulong index, JsValue value, bool throwOnError);
  49. public abstract void DeletePropertyOrThrow(ulong index);
  50. private sealed class ObjectInstanceOperations : ArrayOperations<ObjectInstance>
  51. {
  52. public ObjectInstanceOperations(ObjectInstance target) : base(target)
  53. {
  54. }
  55. private double GetIntegerLength()
  56. {
  57. var descValue = _target.Get("length", _target);
  58. if (!ReferenceEquals(descValue, null))
  59. {
  60. return TypeConverter.ToInteger(descValue);
  61. }
  62. return 0;
  63. }
  64. public override ulong GetSmallestIndex(ulong length)
  65. {
  66. // there are some evil tests that iterate a lot with unshift..
  67. if (_target._properties == null)
  68. {
  69. return 0;
  70. }
  71. var min = length;
  72. foreach (var entry in _target._properties)
  73. {
  74. if (ulong.TryParse(entry.Key, out var index))
  75. {
  76. min = System.Math.Min(index, min);
  77. }
  78. }
  79. if (_target.Prototype?._properties != null)
  80. {
  81. foreach (var entry in _target.Prototype._properties)
  82. {
  83. if (ulong.TryParse(entry.Key, out var index))
  84. {
  85. min = System.Math.Min(index, min);
  86. }
  87. }
  88. }
  89. return min;
  90. }
  91. public override uint GetLength()
  92. {
  93. var integerLength = GetIntegerLength();
  94. return (uint) (integerLength >= 0 ? integerLength : 0);
  95. }
  96. public override ulong GetLongLength()
  97. {
  98. var integerLength = GetIntegerLength();
  99. if (integerLength <= 0)
  100. {
  101. return 0;
  102. }
  103. return (ulong) System.Math.Min(integerLength, MaxArrayLikeLength);
  104. }
  105. public override void SetLength(ulong length)
  106. {
  107. _target.Set("length", length, true);
  108. }
  109. public override void EnsureCapacity(ulong capacity)
  110. {
  111. }
  112. public override JsValue Get(ulong index)
  113. {
  114. return _target.Get(TypeConverter.ToString(index), _target);
  115. }
  116. public override bool TryGetValue(ulong index, out JsValue value)
  117. {
  118. var propertyName = TypeConverter.ToString(index);
  119. var property = _target.GetProperty(propertyName);
  120. var kPresent = property != PropertyDescriptor.Undefined;
  121. value = kPresent ? _target.UnwrapJsValue(property) : JsValue.Undefined;
  122. return kPresent;
  123. }
  124. public override void Set(ulong index, JsValue value, bool throwOnError)
  125. {
  126. _target.Set(TypeConverter.ToString(index), value, throwOnError);
  127. }
  128. public override void DeletePropertyOrThrow(ulong index)
  129. {
  130. _target.DeletePropertyOrThrow(TypeConverter.ToString(index));
  131. }
  132. }
  133. private sealed class ArrayInstanceOperations : ArrayOperations<ArrayInstance>
  134. {
  135. public ArrayInstanceOperations(ArrayInstance target) : base(target)
  136. {
  137. }
  138. public override ulong GetSmallestIndex(ulong length)
  139. {
  140. return _target.GetSmallestIndex();
  141. }
  142. public override uint GetLength()
  143. {
  144. return (uint) ((JsNumber) _target._length._value)._value;
  145. }
  146. public override ulong GetLongLength()
  147. {
  148. return (ulong) ((JsNumber) _target._length._value)._value;
  149. }
  150. public override void SetLength(ulong length)
  151. {
  152. _target.Set("length", length, true);
  153. }
  154. public override void EnsureCapacity(ulong capacity)
  155. {
  156. _target.EnsureCapacity((uint) capacity);
  157. }
  158. public override bool TryGetValue(ulong index, out JsValue value)
  159. {
  160. // array max size is uint
  161. return _target.TryGetValue((uint) index, out value);
  162. }
  163. public override JsValue Get(ulong index)
  164. {
  165. return _target.Get((uint) index);
  166. }
  167. public override JsValue[] GetAll(Types elementTypes)
  168. {
  169. var n = _target.Length;
  170. if (_target._dense == null || _target._dense.Length < n)
  171. {
  172. return base.GetAll(elementTypes);
  173. }
  174. // optimized
  175. var jsValues = new JsValue[n];
  176. for (uint i = 0; i < (uint) jsValues.Length; i++)
  177. {
  178. var prop = _target._dense[i] ?? PropertyDescriptor.Undefined;
  179. if (prop == PropertyDescriptor.Undefined)
  180. {
  181. prop = _target.Prototype?.GetProperty(i) ?? PropertyDescriptor.Undefined;
  182. }
  183. var jsValue = _target.UnwrapJsValue(prop);
  184. if ((jsValue.Type & elementTypes) == 0)
  185. {
  186. ExceptionHelper.ThrowTypeErrorNoEngine<object>("invalid type");
  187. }
  188. jsValues[i] = jsValue;
  189. }
  190. return jsValues;
  191. }
  192. public override void DeletePropertyOrThrow(ulong index)
  193. {
  194. _target.DeleteAt((uint) index);
  195. }
  196. public override void Set(ulong index, JsValue value, bool throwOnError)
  197. {
  198. _target.SetIndexValue((uint) index, value, throwOnError);
  199. }
  200. }
  201. }
  202. /// <summary>
  203. /// Adapter to use optimized array operations when possible.
  204. /// Gaps the difference between ArgumentsInstance and ArrayInstance.
  205. /// </summary>
  206. internal abstract class ArrayOperations<T> : ArrayOperations where T : ObjectInstance
  207. {
  208. protected readonly T _target;
  209. protected ArrayOperations(T target)
  210. {
  211. _target = target;
  212. }
  213. public override ObjectInstance Target => _target;
  214. }
  215. }