ArrayConstructor.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. using System.Collections;
  2. using System.Runtime.CompilerServices;
  3. using Jint.Native.Function;
  4. using Jint.Native.Object;
  5. using Jint.Native.Symbol;
  6. using Jint.Runtime;
  7. using Jint.Runtime.Descriptors;
  8. using Jint.Runtime.Descriptors.Specialized;
  9. using Jint.Runtime.Interop;
  10. namespace Jint.Native.Array
  11. {
  12. public sealed class ArrayConstructor : FunctionInstance, IConstructor
  13. {
  14. private ArrayConstructor(Engine engine) : base(engine, "Array", null, null, false)
  15. {
  16. }
  17. public ArrayPrototype PrototypeObject { get; private set; }
  18. public static ArrayConstructor CreateArrayConstructor(Engine engine)
  19. {
  20. var obj = new ArrayConstructor(engine);
  21. obj.Extensible = true;
  22. // The value of the [[Prototype]] internal property of the Array constructor is the Function prototype object
  23. obj.Prototype = engine.Function.PrototypeObject;
  24. obj.PrototypeObject = ArrayPrototype.CreatePrototypeObject(engine, obj);
  25. obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
  26. // The initial value of Array.prototype is the Array prototype object
  27. obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
  28. obj.SetOwnProperty(GlobalSymbolRegistry.Species._value,
  29. new GetSetPropertyDescriptor(
  30. get: new ClrFunctionInstance(engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable),
  31. set: Undefined,
  32. PropertyFlag.Configurable));
  33. return obj;
  34. }
  35. public void Configure()
  36. {
  37. SetOwnProperty("from",new PropertyDescriptor(new ClrFunctionInstance(Engine, "from", From, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable));
  38. SetOwnProperty("isArray", new PropertyDescriptor(new ClrFunctionInstance(Engine, "isArray", IsArray, 1), PropertyFlag.NonEnumerable));
  39. SetOwnProperty("of", new PropertyDescriptor(new ClrFunctionInstance(Engine, "of", Of, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable));
  40. }
  41. private JsValue From(JsValue thisObj, JsValue[] arguments)
  42. {
  43. var source = arguments.At(0);
  44. if (source is IArrayLike arrayLike)
  45. {
  46. arrayLike.ToArray(_engine);
  47. }
  48. return Undefined;
  49. }
  50. private JsValue Of(JsValue thisObj, JsValue[] arguments)
  51. {
  52. return Undefined;
  53. }
  54. private static JsValue Species(JsValue thisObject, JsValue[] arguments)
  55. {
  56. return thisObject;
  57. }
  58. private static JsValue IsArray(JsValue thisObj, JsValue[] arguments)
  59. {
  60. if (arguments.Length == 0)
  61. {
  62. return false;
  63. }
  64. var o = arguments.At(0);
  65. return o.IsObject() && o.AsObject().Class == "Array";
  66. }
  67. public override JsValue Call(JsValue thisObject, JsValue[] arguments)
  68. {
  69. return Construct(arguments);
  70. }
  71. public ObjectInstance Construct(JsValue[] arguments)
  72. {
  73. // check if we can figure out good size
  74. var capacity = arguments.Length > 0 ? (uint) arguments.Length : 0;
  75. if (arguments.Length == 1 && arguments[0].Type == Types.Number)
  76. {
  77. var number = ((JsNumber) arguments[0])._value;
  78. if (number > 0)
  79. {
  80. capacity = (uint) number;
  81. }
  82. }
  83. return Construct(arguments, capacity);
  84. }
  85. public ArrayInstance Construct(int capacity)
  86. {
  87. if (capacity < 0)
  88. {
  89. ExceptionHelper.ThrowArgumentException("invalid array length", nameof(capacity));
  90. }
  91. return Construct(System.Array.Empty<JsValue>(), (uint) capacity);
  92. }
  93. public ArrayInstance Construct(uint capacity)
  94. {
  95. return Construct(System.Array.Empty<JsValue>(), capacity);
  96. }
  97. public ArrayInstance Construct(JsValue[] arguments, uint capacity)
  98. {
  99. var instance = new ArrayInstance(Engine, capacity);
  100. instance.Prototype = PrototypeObject;
  101. instance.Extensible = true;
  102. if (arguments.Length == 1 && arguments.At(0).IsNumber())
  103. {
  104. var length = TypeConverter.ToUint32(arguments.At(0));
  105. if (((JsNumber) arguments[0])._value != length)
  106. {
  107. ExceptionHelper.ThrowRangeError(_engine, "Invalid array length");
  108. }
  109. instance._length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
  110. }
  111. else if (arguments.Length == 1 && arguments[0] is ObjectWrapper objectWrapper)
  112. {
  113. if (objectWrapper.Target is IEnumerable enumerable)
  114. {
  115. var jsArray = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
  116. var tempArray = _engine._jsValueArrayPool.RentArray(1);
  117. foreach (var item in enumerable)
  118. {
  119. var jsItem = FromObject(Engine, item);
  120. tempArray[0] = jsItem;
  121. Engine.Array.PrototypeObject.Push(jsArray, tempArray);
  122. }
  123. _engine._jsValueArrayPool.ReturnArray(tempArray);
  124. return jsArray;
  125. }
  126. }
  127. else
  128. {
  129. instance._length = new PropertyDescriptor(0, PropertyFlag.OnlyWritable);
  130. if (arguments.Length > 0)
  131. {
  132. PrototypeObject.Push(instance, arguments);
  133. }
  134. }
  135. return instance;
  136. }
  137. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  138. internal ArrayInstance ConstructFast(uint length)
  139. {
  140. var instance = new ArrayInstance(Engine, length)
  141. {
  142. Prototype = PrototypeObject,
  143. Extensible = true,
  144. _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable)
  145. };
  146. return instance;
  147. }
  148. }
  149. }