ArrayConstructor.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. using System.Collections;
  2. using System.Runtime.CompilerServices;
  3. using Jint.Native.Function;
  4. using Jint.Native.Iterator;
  5. using Jint.Native.Object;
  6. using Jint.Native.Symbol;
  7. using Jint.Runtime;
  8. using Jint.Runtime.Descriptors;
  9. using Jint.Runtime.Descriptors.Specialized;
  10. using Jint.Runtime.Interop;
  11. namespace Jint.Native.Array
  12. {
  13. public sealed class ArrayConstructor : FunctionInstance, IConstructor
  14. {
  15. private ArrayConstructor(Engine engine) : base(engine, "Array", null, null, false)
  16. {
  17. }
  18. public ArrayPrototype PrototypeObject { get; private set; }
  19. public static ArrayConstructor CreateArrayConstructor(Engine engine)
  20. {
  21. var obj = new ArrayConstructor(engine);
  22. obj.Extensible = true;
  23. // The value of the [[Prototype]] internal property of the Array constructor is the Function prototype object
  24. obj.Prototype = engine.Function.PrototypeObject;
  25. obj.PrototypeObject = ArrayPrototype.CreatePrototypeObject(engine, obj);
  26. obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.Configurable));
  27. // The initial value of Array.prototype is the Array prototype object
  28. obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
  29. obj.SetOwnProperty(GlobalSymbolRegistry.Species._value,
  30. new GetSetPropertyDescriptor(
  31. get: new ClrFunctionInstance(engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable),
  32. set: Undefined,
  33. PropertyFlag.Configurable));
  34. return obj;
  35. }
  36. public void Configure()
  37. {
  38. SetOwnProperty("from",new PropertyDescriptor(new ClrFunctionInstance(Engine, "from", From, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable));
  39. SetOwnProperty("isArray", new PropertyDescriptor(new ClrFunctionInstance(Engine, "isArray", IsArray, 1), PropertyFlag.NonEnumerable));
  40. SetOwnProperty("of", new PropertyDescriptor(new ClrFunctionInstance(Engine, "of", Of, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable));
  41. }
  42. private JsValue From(JsValue thisObj, JsValue[] arguments)
  43. {
  44. var source = arguments.At(0);
  45. var mapFunction = arguments.At(1);
  46. var callable = !mapFunction.IsUndefined() ? GetCallable(mapFunction) : null;
  47. var thisArg = arguments.At(2);
  48. if (source.IsNullOrUndefined())
  49. {
  50. ExceptionHelper.ThrowTypeError(_engine, "Cannot convert undefined or null to object");
  51. }
  52. if (source is JsString jsString)
  53. {
  54. var a = _engine.Array.ConstructFast((uint) jsString.Length);
  55. for (int i = 0; i < jsString._value.Length; i++)
  56. {
  57. a.SetIndexValue((uint) i, JsString.Create(jsString._value[i]), updateLength: false);
  58. }
  59. return a;
  60. }
  61. if (thisObj.IsNull() || !(source is ObjectInstance objectInstance))
  62. {
  63. return _engine.Array.ConstructFast(0);
  64. }
  65. if (objectInstance.IsArrayLike)
  66. {
  67. return ConstructArrayFromArrayLike(objectInstance, callable, thisArg);
  68. }
  69. if (objectInstance is IObjectWrapper wrapper && wrapper.Target is IEnumerable enumerable)
  70. {
  71. return ConstructArrayFromIEnumerable(enumerable);
  72. }
  73. var instance = _engine.Array.ConstructFast(0);
  74. if (objectInstance.TryGetIterator(_engine, out var iterator))
  75. {
  76. var protocol = new ArrayProtocol(_engine, thisArg, instance, iterator, callable);
  77. protocol.Execute();
  78. }
  79. return instance;
  80. }
  81. private ArrayInstance ConstructArrayFromArrayLike(
  82. ObjectInstance objectInstance,
  83. ICallable callable,
  84. JsValue thisArg)
  85. {
  86. var operations = ArrayPrototype.ArrayOperations.For(objectInstance);
  87. var length = operations.GetLength();
  88. var a = _engine.Array.ConstructFast(length);
  89. var args = !ReferenceEquals(callable, null)
  90. ? _engine._jsValueArrayPool.RentArray(2)
  91. : null;
  92. uint n = 0;
  93. for (uint i = 0; i < length; i++)
  94. {
  95. JsValue jsValue;
  96. operations.TryGetValue(i, out var value);
  97. if (!ReferenceEquals(callable, null))
  98. {
  99. args[0] = value;
  100. args[1] = i;
  101. jsValue = callable.Call(thisArg, args);
  102. // function can alter data
  103. length = operations.GetLength();
  104. }
  105. else
  106. {
  107. jsValue = value;
  108. }
  109. a.SetIndexValue(i, jsValue, updateLength: false);
  110. n++;
  111. }
  112. if (!ReferenceEquals(callable, null))
  113. {
  114. _engine._jsValueArrayPool.ReturnArray(args);
  115. }
  116. a.SetLength(length);
  117. return a;
  118. }
  119. internal sealed class ArrayProtocol : IteratorProtocol
  120. {
  121. private readonly JsValue _thisArg;
  122. private readonly ArrayInstance _instance;
  123. private readonly ICallable _callable;
  124. private long _index = -1;
  125. public ArrayProtocol(
  126. Engine engine,
  127. JsValue thisArg,
  128. ArrayInstance instance,
  129. IIterator iterator,
  130. ICallable callable) : base(engine, iterator, 2)
  131. {
  132. _thisArg = thisArg;
  133. _instance = instance;
  134. _callable = callable;
  135. }
  136. protected override void ProcessItem(JsValue[] args, JsValue currentValue)
  137. {
  138. _index++;
  139. var sourceValue = ExtractValueFromIteratorInstance(currentValue);
  140. JsValue jsValue;
  141. if (!ReferenceEquals(_callable, null))
  142. {
  143. args[0] = sourceValue;
  144. args[1] = _index;
  145. jsValue = _callable.Call(_thisArg, args);
  146. }
  147. else
  148. {
  149. jsValue = sourceValue;
  150. }
  151. _instance.SetIndexValue((uint) _index, jsValue, updateLength: false);
  152. }
  153. protected override void IterationEnd()
  154. {
  155. _instance.SetLength((uint) (_index + 1));
  156. }
  157. }
  158. private JsValue Of(JsValue thisObj, JsValue[] arguments)
  159. {
  160. return _engine.Array.Construct(arguments);
  161. }
  162. private static JsValue Species(JsValue thisObject, JsValue[] arguments)
  163. {
  164. return thisObject;
  165. }
  166. private static JsValue IsArray(JsValue thisObj, JsValue[] arguments)
  167. {
  168. if (arguments.Length == 0)
  169. {
  170. return false;
  171. }
  172. var o = arguments.At(0);
  173. return o.IsObject() && o.AsObject().Class == "Array";
  174. }
  175. public override JsValue Call(JsValue thisObject, JsValue[] arguments)
  176. {
  177. return Construct(arguments);
  178. }
  179. public ObjectInstance Construct(JsValue[] arguments)
  180. {
  181. // check if we can figure out good size
  182. var capacity = arguments.Length > 0 ? (uint) arguments.Length : 0;
  183. if (arguments.Length == 1 && arguments[0].Type == Types.Number)
  184. {
  185. var number = ((JsNumber) arguments[0])._value;
  186. if (number > 0)
  187. {
  188. capacity = (uint) number;
  189. }
  190. }
  191. return Construct(arguments, capacity);
  192. }
  193. public ArrayInstance Construct(int capacity)
  194. {
  195. if (capacity < 0)
  196. {
  197. ExceptionHelper.ThrowArgumentException("invalid array length", nameof(capacity));
  198. }
  199. return Construct(System.ArrayExt.Empty<JsValue>(), (uint) capacity);
  200. }
  201. public ArrayInstance Construct(uint capacity)
  202. {
  203. return Construct(System.ArrayExt.Empty<JsValue>(), capacity);
  204. }
  205. public ArrayInstance Construct(JsValue[] arguments, uint capacity)
  206. {
  207. var instance = new ArrayInstance(Engine, capacity);
  208. instance.Prototype = PrototypeObject;
  209. instance.Extensible = true;
  210. if (arguments.Length == 1 && arguments.At(0).IsNumber())
  211. {
  212. var length = TypeConverter.ToUint32(arguments.At(0));
  213. if (((JsNumber) arguments[0])._value != length)
  214. {
  215. ExceptionHelper.ThrowRangeError(_engine, "Invalid array length");
  216. }
  217. instance._length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
  218. }
  219. else if (arguments.Length == 1 && arguments[0] is IObjectWrapper objectWrapper)
  220. {
  221. if (objectWrapper.Target is IEnumerable enumerable)
  222. {
  223. return ConstructArrayFromIEnumerable(enumerable);
  224. }
  225. }
  226. else if (arguments.Length == 1 && arguments[0] is ArrayInstance arrayInstance)
  227. {
  228. // direct copy
  229. return ConstructArrayFromArrayLike(arrayInstance, null, this);
  230. }
  231. else
  232. {
  233. instance._length = new PropertyDescriptor(0, PropertyFlag.OnlyWritable);
  234. if (arguments.Length > 0)
  235. {
  236. PrototypeObject.Push(instance, arguments);
  237. }
  238. }
  239. return instance;
  240. }
  241. private ArrayInstance ConstructArrayFromIEnumerable(IEnumerable enumerable)
  242. {
  243. var jsArray = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
  244. var tempArray = _engine._jsValueArrayPool.RentArray(1);
  245. foreach (var item in enumerable)
  246. {
  247. var jsItem = FromObject(Engine, item);
  248. tempArray[0] = jsItem;
  249. Engine.Array.PrototypeObject.Push(jsArray, tempArray);
  250. }
  251. _engine._jsValueArrayPool.ReturnArray(tempArray);
  252. return jsArray;
  253. }
  254. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  255. internal ArrayInstance ConstructFast(uint length)
  256. {
  257. var instance = new ArrayInstance(Engine, length)
  258. {
  259. Prototype = PrototypeObject,
  260. Extensible = true,
  261. _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable)
  262. };
  263. return instance;
  264. }
  265. }
  266. }