ObjectWrapper.Specialized.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. using System.Collections;
  2. using System.Diagnostics.CodeAnalysis;
  3. using System.Globalization;
  4. using Jint.Extensions;
  5. using Jint.Native;
  6. namespace Jint.Runtime.Interop;
  7. internal abstract class ArrayLikeWrapper : ObjectWrapper
  8. {
  9. #pragma warning disable CS0618 // Type or member is obsolete
  10. protected ArrayLikeWrapper(
  11. Engine engine,
  12. object obj,
  13. Type itemType,
  14. Type? type = null) : base(engine, obj, type)
  15. #pragma warning restore CS0618 // Type or member is obsolete
  16. {
  17. ItemType = itemType;
  18. if (engine.Options.Interop.AttachArrayPrototype)
  19. {
  20. Prototype = engine.Intrinsics.Array.PrototypeObject;
  21. }
  22. }
  23. [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)]
  24. private Type ItemType { get; }
  25. public abstract int Length { get; }
  26. public override JsValue Get(JsValue property, JsValue receiver)
  27. {
  28. if (property.IsInteger())
  29. {
  30. return FromObject(_engine, GetAt(property.AsInteger()));
  31. }
  32. return base.Get(property, receiver);
  33. }
  34. public override bool HasProperty(JsValue property)
  35. {
  36. if (property.IsNumber())
  37. {
  38. var value = ((JsNumber) property)._value;
  39. if (TypeConverter.IsIntegralNumber(value))
  40. {
  41. var index = (int) value;
  42. if (Target is ICollection collection && index < collection.Count)
  43. {
  44. return true;
  45. }
  46. }
  47. }
  48. return base.HasProperty(property);
  49. }
  50. public override bool Delete(JsValue property)
  51. {
  52. if (!_engine.Options.Interop.AllowWrite)
  53. {
  54. return false;
  55. }
  56. if (property.IsNumber())
  57. {
  58. var value = ((JsNumber) property)._value;
  59. if (TypeConverter.IsIntegralNumber(value))
  60. {
  61. DoSetAt((int) value, default);
  62. return true;
  63. }
  64. }
  65. return base.Delete(property);
  66. }
  67. public abstract object? GetAt(int index);
  68. public void SetAt(int index, JsValue value)
  69. {
  70. if (_engine.Options.Interop.AllowWrite)
  71. {
  72. EnsureCapacity(index);
  73. DoSetAt(index, ConvertToItemType(value));
  74. }
  75. }
  76. protected abstract void DoSetAt(int index, object? value);
  77. public abstract void AddDefault();
  78. public abstract void Add(JsValue value);
  79. public abstract void RemoveAt(int index);
  80. public virtual void EnsureCapacity(int capacity)
  81. {
  82. while (Length < capacity)
  83. {
  84. AddDefault();
  85. }
  86. }
  87. protected object? ConvertToItemType(JsValue value)
  88. {
  89. object? converted;
  90. if (ItemType == typeof(JsValue))
  91. {
  92. converted = value;
  93. }
  94. else if (!ReflectionExtensions.TryConvertViaTypeCoercion(ItemType, Engine.Options.Interop.ValueCoercion, value, out converted))
  95. {
  96. // attempt to convert the JsValue to the target type
  97. converted = value.ToObject();
  98. if (converted != null && converted.GetType() != ItemType)
  99. {
  100. converted = Engine.TypeConverter.Convert(converted, ItemType, CultureInfo.InvariantCulture);
  101. }
  102. }
  103. return converted;
  104. }
  105. }
  106. internal class ListWrapper : ArrayLikeWrapper
  107. {
  108. private readonly IList? _list;
  109. internal ListWrapper(Engine engine, IList target, Type type)
  110. : base(engine, target, typeof(object), type)
  111. {
  112. _list = target;
  113. }
  114. public override int Length => _list?.Count ?? 0;
  115. public override object? GetAt(int index)
  116. {
  117. if (_list is not null && index >= 0 && index < _list.Count)
  118. {
  119. return _list[index];
  120. }
  121. return null;
  122. }
  123. protected override void DoSetAt(int index, object? value)
  124. {
  125. if (_list is not null)
  126. {
  127. _list[index] = value;
  128. }
  129. }
  130. public override void AddDefault() => _list?.Add(null);
  131. public override void Add(JsValue value) => _list?.Add(ConvertToItemType(value));
  132. public override void RemoveAt(int index) => _list?.RemoveAt(index);
  133. }
  134. internal class GenericListWrapper<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] T> : ArrayLikeWrapper
  135. {
  136. private readonly IList<T> _list;
  137. public GenericListWrapper(Engine engine, IList<T> target, Type? type)
  138. : base(engine, target, typeof(T), type)
  139. {
  140. _list = target;
  141. }
  142. public override int Length => _list.Count;
  143. public override object? GetAt(int index)
  144. {
  145. if (index >= 0 && index < _list.Count)
  146. {
  147. return _list[index];
  148. }
  149. return null;
  150. }
  151. protected override void DoSetAt(int index, object? value) => _list[index] = (T) value!;
  152. public override void AddDefault() => _list.Add(default!);
  153. public override void Add(JsValue value)
  154. {
  155. var converted = ConvertToItemType(value);
  156. _list.Add((T) converted!);
  157. }
  158. public override void RemoveAt(int index) => _list.RemoveAt(index);
  159. }
  160. internal sealed class ReadOnlyListWrapper<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] T> : ArrayLikeWrapper
  161. {
  162. private readonly IReadOnlyList<T> _list;
  163. public ReadOnlyListWrapper(Engine engine, IReadOnlyList<T> target, Type type) : base(engine, target, typeof(T), type)
  164. {
  165. _list = target;
  166. }
  167. public override int Length => _list.Count;
  168. public override object? GetAt(int index)
  169. {
  170. if (index >= 0 && index < _list.Count)
  171. {
  172. return _list[index];
  173. }
  174. return null;
  175. }
  176. public override void AddDefault() => ExceptionHelper.ThrowNotSupportedException();
  177. protected override void DoSetAt(int index, object? value) => ExceptionHelper.ThrowNotSupportedException();
  178. public override void Add(JsValue value) => ExceptionHelper.ThrowNotSupportedException();
  179. public override void RemoveAt(int index) => ExceptionHelper.ThrowNotSupportedException();
  180. public override void EnsureCapacity(int capacity) => ExceptionHelper.ThrowNotSupportedException();
  181. }