ArrayConstructor.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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. var operations = ArrayPrototype.ArrayOperations.For(objectInstance);
  68. var length = operations.GetLength();
  69. var a = _engine.Array.ConstructFast(length);
  70. var args = !ReferenceEquals(callable, null)
  71. ? _engine._jsValueArrayPool.RentArray(2)
  72. : null;
  73. uint n = 0;
  74. for (uint i = 0; i < length; i++)
  75. {
  76. JsValue jsValue;
  77. operations.TryGetValue(i, out var value);
  78. if (!ReferenceEquals(callable, null))
  79. {
  80. args[0] = value;
  81. args[1] = i;
  82. jsValue = callable.Call(thisArg, args);
  83. // function can alter data
  84. length = operations.GetLength();
  85. }
  86. else
  87. {
  88. jsValue = value;
  89. }
  90. a.SetIndexValue(i, jsValue, updateLength: false);
  91. n++;
  92. }
  93. if (!ReferenceEquals(callable, null))
  94. {
  95. _engine._jsValueArrayPool.ReturnArray(args);
  96. }
  97. a.SetLength(length);
  98. return a;
  99. }
  100. var instance = _engine.Array.ConstructFast(0);
  101. if (objectInstance.TryGetIterator(_engine, out var iterator))
  102. {
  103. var protocol = new ArrayProtocol(_engine, thisArg, instance, iterator, callable);
  104. protocol.Execute();
  105. }
  106. return instance;
  107. }
  108. private sealed class ArrayProtocol : IteratorProtocol
  109. {
  110. private readonly JsValue _thisArg;
  111. private readonly ArrayInstance _instance;
  112. private readonly ICallable _callable;
  113. private long _index = -1;
  114. public ArrayProtocol(
  115. Engine engine,
  116. JsValue thisArg,
  117. ArrayInstance instance,
  118. IIterator iterator,
  119. ICallable callable) : base(engine, iterator, 2)
  120. {
  121. _thisArg = thisArg;
  122. _instance = instance;
  123. _callable = callable;
  124. }
  125. protected override void ProcessItem(JsValue[] args, JsValue currentValue)
  126. {
  127. _index++;
  128. var sourceValue = ExtractValueFromIteratorInstance(currentValue);
  129. JsValue jsValue;
  130. if (!ReferenceEquals(_callable, null))
  131. {
  132. args[0] = sourceValue;
  133. args[1] = _index;
  134. jsValue = _callable.Call(_thisArg, args);
  135. }
  136. else
  137. {
  138. jsValue = sourceValue;
  139. }
  140. _instance.SetIndexValue((uint) _index, jsValue, updateLength: false);
  141. }
  142. protected override void IterationEnd()
  143. {
  144. _instance.SetLength((uint) (_index + 1));
  145. }
  146. }
  147. private JsValue Of(JsValue thisObj, JsValue[] arguments)
  148. {
  149. return _engine.Array.Construct(arguments);
  150. }
  151. private static JsValue Species(JsValue thisObject, JsValue[] arguments)
  152. {
  153. return thisObject;
  154. }
  155. private static JsValue IsArray(JsValue thisObj, JsValue[] arguments)
  156. {
  157. if (arguments.Length == 0)
  158. {
  159. return false;
  160. }
  161. var o = arguments.At(0);
  162. return o.IsObject() && o.AsObject().Class == "Array";
  163. }
  164. public override JsValue Call(JsValue thisObject, JsValue[] arguments)
  165. {
  166. return Construct(arguments);
  167. }
  168. public ObjectInstance Construct(JsValue[] arguments)
  169. {
  170. // check if we can figure out good size
  171. var capacity = arguments.Length > 0 ? (uint) arguments.Length : 0;
  172. if (arguments.Length == 1 && arguments[0].Type == Types.Number)
  173. {
  174. var number = ((JsNumber) arguments[0])._value;
  175. if (number > 0)
  176. {
  177. capacity = (uint) number;
  178. }
  179. }
  180. return Construct(arguments, capacity);
  181. }
  182. public ArrayInstance Construct(int capacity)
  183. {
  184. if (capacity < 0)
  185. {
  186. ExceptionHelper.ThrowArgumentException("invalid array length", nameof(capacity));
  187. }
  188. return Construct(System.ArrayExt.Empty<JsValue>(), (uint) capacity);
  189. }
  190. public ArrayInstance Construct(uint capacity)
  191. {
  192. return Construct(System.ArrayExt.Empty<JsValue>(), capacity);
  193. }
  194. public ArrayInstance Construct(JsValue[] arguments, uint capacity)
  195. {
  196. var instance = new ArrayInstance(Engine, capacity);
  197. instance.Prototype = PrototypeObject;
  198. instance.Extensible = true;
  199. if (arguments.Length == 1 && arguments.At(0).IsNumber())
  200. {
  201. var length = TypeConverter.ToUint32(arguments.At(0));
  202. if (((JsNumber) arguments[0])._value != length)
  203. {
  204. ExceptionHelper.ThrowRangeError(_engine, "Invalid array length");
  205. }
  206. instance._length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
  207. }
  208. else if (arguments.Length == 1 && arguments[0] is ObjectWrapper objectWrapper)
  209. {
  210. if (objectWrapper.Target is IEnumerable enumerable)
  211. {
  212. var jsArray = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
  213. var tempArray = _engine._jsValueArrayPool.RentArray(1);
  214. foreach (var item in enumerable)
  215. {
  216. var jsItem = FromObject(Engine, item);
  217. tempArray[0] = jsItem;
  218. Engine.Array.PrototypeObject.Push(jsArray, tempArray);
  219. }
  220. _engine._jsValueArrayPool.ReturnArray(tempArray);
  221. return jsArray;
  222. }
  223. }
  224. else
  225. {
  226. instance._length = new PropertyDescriptor(0, PropertyFlag.OnlyWritable);
  227. if (arguments.Length > 0)
  228. {
  229. PrototypeObject.Push(instance, arguments);
  230. }
  231. }
  232. return instance;
  233. }
  234. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  235. internal ArrayInstance ConstructFast(uint length)
  236. {
  237. var instance = new ArrayInstance(Engine, length)
  238. {
  239. Prototype = PrototypeObject,
  240. Extensible = true,
  241. _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable)
  242. };
  243. return instance;
  244. }
  245. }
  246. }