JsString.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. using System;
  2. using System.Text;
  3. using Jint.Native.Array;
  4. using Jint.Runtime;
  5. namespace Jint.Native
  6. {
  7. public class JsString : JsValue, IEquatable<JsString>
  8. {
  9. private const int AsciiMax = 126;
  10. private static readonly JsString[] _charToJsValue;
  11. private static readonly JsString[] _charToStringJsValue;
  12. private static readonly JsString[] _intToStringJsValue;
  13. public static readonly JsString Empty = new JsString("");
  14. private static readonly JsString NullString = new JsString("null");
  15. internal static readonly JsString UndefinedString = new JsString("undefined");
  16. internal static readonly JsString ObjectString = new JsString("object");
  17. internal static readonly JsString FunctionString = new JsString("function");
  18. internal static readonly JsString BooleanString = new JsString("boolean");
  19. internal static readonly JsString StringString = new JsString("string");
  20. internal static readonly JsString NumberString = new JsString("number");
  21. internal static readonly JsString SymbolString = new JsString("symbol");
  22. internal static readonly JsString DefaultString = new JsString("default");
  23. internal static readonly JsString NumberZeroString = new JsString("0");
  24. internal static readonly JsString NumberOneString = new JsString("1");
  25. internal static readonly JsString TrueString = new JsString("true");
  26. internal static readonly JsString FalseString = new JsString("false");
  27. internal static readonly JsString LengthString = new JsString("length");
  28. internal string _value;
  29. static JsString()
  30. {
  31. _charToJsValue = new JsString[AsciiMax + 1];
  32. _charToStringJsValue = new JsString[AsciiMax + 1];
  33. for (var i = 0; i <= AsciiMax; i++)
  34. {
  35. _charToJsValue[i] = new JsString((char) i);
  36. _charToStringJsValue[i] = new JsString(((char) i).ToString());
  37. }
  38. _intToStringJsValue = new JsString[1024];
  39. for (var i = 0; i < _intToStringJsValue.Length; ++i)
  40. {
  41. _intToStringJsValue[i] = new JsString(TypeConverter.ToString(i));
  42. }
  43. }
  44. public JsString(string value) : this(value, InternalTypes.String)
  45. {
  46. }
  47. private JsString(string value, InternalTypes type) : base(type)
  48. {
  49. _value = value;
  50. }
  51. public override object ToObject()
  52. {
  53. return _value;
  54. }
  55. public JsString(char value) : base(Types.String)
  56. {
  57. _value = value.ToString();
  58. }
  59. public static bool operator ==(JsValue a, JsString b)
  60. {
  61. if (a is JsString s && b is object)
  62. {
  63. return s.ToString() == b.ToString();
  64. }
  65. if ((object) a == null)
  66. {
  67. return (object) b == null;
  68. }
  69. return (object) b != null && a.Equals(b);
  70. }
  71. public static bool operator ==(JsString a, JsValue b)
  72. {
  73. if (a is object && b is JsString s)
  74. {
  75. return s.ToString() == b.ToString();
  76. }
  77. if ((object) a == null)
  78. {
  79. return (object) b == null;
  80. }
  81. return (object) b != null && a.Equals(b);
  82. }
  83. public static bool operator !=(JsString a, JsValue b)
  84. {
  85. return !(a == b);
  86. }
  87. public static bool operator !=(JsValue a, JsString b)
  88. {
  89. return !(a == b);
  90. }
  91. public virtual char this[int index] => _value[index];
  92. public virtual JsString Append(JsValue jsValue)
  93. {
  94. return new ConcatenatedString(string.Concat(_value, TypeConverter.ToString(jsValue)));
  95. }
  96. internal virtual JsString EnsureCapacity(int capacity)
  97. {
  98. return new ConcatenatedString(_value, capacity);
  99. }
  100. internal virtual bool IsNullOrEmpty()
  101. {
  102. return string.IsNullOrEmpty(_value);
  103. }
  104. public virtual int Length => _value.Length;
  105. internal static JsString Create(string value)
  106. {
  107. if (value.Length > 1)
  108. {
  109. return new JsString(value);
  110. }
  111. if (value.Length == 0)
  112. {
  113. return Empty;
  114. }
  115. var i = (uint) value[0];
  116. if (i < (uint) _charToStringJsValue.Length)
  117. {
  118. return _charToStringJsValue[i];
  119. }
  120. return new JsString(value);
  121. }
  122. internal static JsString Create(char value)
  123. {
  124. if (value < (uint) _charToJsValue.Length)
  125. {
  126. return _charToJsValue[value];
  127. }
  128. return new JsString(value);
  129. }
  130. internal static JsString Create(int value)
  131. {
  132. if (value < (uint) _intToStringJsValue.Length)
  133. {
  134. return _intToStringJsValue[value];
  135. }
  136. return new JsString(TypeConverter.ToString(value));
  137. }
  138. internal static JsValue Create(uint value)
  139. {
  140. if (value < (uint) _intToStringJsValue.Length)
  141. {
  142. return _intToStringJsValue[value];
  143. }
  144. return new JsString(TypeConverter.ToString(value));
  145. }
  146. internal static JsValue Create(ulong value)
  147. {
  148. if (value < (uint) _intToStringJsValue.Length)
  149. {
  150. return _intToStringJsValue[value];
  151. }
  152. return new JsString(TypeConverter.ToString(value));
  153. }
  154. public override string ToString()
  155. {
  156. return _value;
  157. }
  158. public ArrayInstance ToArray(Engine engine)
  159. {
  160. var array = engine.Realm.Intrinsics.Array.ConstructFast((uint) _value.Length);
  161. for (int i = 0; i < _value.Length; ++i)
  162. {
  163. array.SetIndexValue((uint) i, _value[i], updateLength: false);
  164. }
  165. return array;
  166. }
  167. internal int IndexOf(string value, StringComparison comparisonType)
  168. {
  169. return ToString().IndexOf(value, comparisonType);
  170. }
  171. internal int IndexOf(char value)
  172. {
  173. return ToString().IndexOf(value);
  174. }
  175. internal string Substring(int startIndex, int length)
  176. {
  177. return ToString().Substring(startIndex, length);
  178. }
  179. internal string Substring(int startIndex)
  180. {
  181. return ToString().Substring(startIndex);
  182. }
  183. public override bool Equals(JsValue obj)
  184. {
  185. if (ReferenceEquals(null, obj))
  186. {
  187. return false;
  188. }
  189. if (!(obj is JsString s))
  190. {
  191. return false;
  192. }
  193. return Equals(s);
  194. }
  195. public bool Equals(JsString other)
  196. {
  197. if (ReferenceEquals(null, other))
  198. {
  199. return false;
  200. }
  201. if (ReferenceEquals(this, other))
  202. {
  203. return true;
  204. }
  205. return _value == other.ToString();
  206. }
  207. public override bool Equals(object obj)
  208. {
  209. return ReferenceEquals(this, obj) || obj is JsString other && Equals(other);
  210. }
  211. public override int GetHashCode()
  212. {
  213. return _value.GetHashCode();
  214. }
  215. internal sealed class ConcatenatedString : JsString
  216. {
  217. private StringBuilder _stringBuilder;
  218. private bool _dirty;
  219. internal ConcatenatedString(string value, int capacity = 0)
  220. : base(value, InternalTypes.String | InternalTypes.RequiresCloning)
  221. {
  222. if (capacity > 0)
  223. {
  224. _stringBuilder = new StringBuilder(value, capacity);
  225. }
  226. else
  227. {
  228. _value = value;
  229. }
  230. }
  231. public override string ToString()
  232. {
  233. if (_dirty)
  234. {
  235. _value = _stringBuilder.ToString();
  236. _dirty = false;
  237. }
  238. return _value;
  239. }
  240. public override char this[int index] => _stringBuilder?[index] ?? _value[index];
  241. public override JsString Append(JsValue jsValue)
  242. {
  243. var value = TypeConverter.ToString(jsValue);
  244. if (_stringBuilder == null)
  245. {
  246. _stringBuilder = new StringBuilder(_value, _value.Length + value.Length);
  247. }
  248. _stringBuilder.Append(value);
  249. _dirty = true;
  250. return this;
  251. }
  252. internal override JsString EnsureCapacity(int capacity)
  253. {
  254. _stringBuilder.EnsureCapacity(capacity);
  255. return this;
  256. }
  257. internal override bool IsNullOrEmpty()
  258. {
  259. return _stringBuilder == null && string.IsNullOrEmpty(_value)
  260. || _stringBuilder != null && _stringBuilder.Length == 0;
  261. }
  262. public override int Length => _stringBuilder?.Length ?? _value?.Length ?? 0;
  263. public override object ToObject() => ToString();
  264. public override bool Equals(JsValue other)
  265. {
  266. if (other is ConcatenatedString cs)
  267. {
  268. if (_stringBuilder != null && cs._stringBuilder != null)
  269. {
  270. return _stringBuilder.Equals(cs._stringBuilder);
  271. }
  272. return ToString() == cs.ToString();
  273. }
  274. if (other is JsString jsString)
  275. {
  276. if (jsString._value.Length != Length)
  277. {
  278. return false;
  279. }
  280. return ToString() == jsString._value;
  281. }
  282. return base.Equals(other);
  283. }
  284. public override int GetHashCode()
  285. {
  286. return _stringBuilder?.GetHashCode() ?? _value.GetHashCode();
  287. }
  288. internal override JsValue DoClone()
  289. {
  290. return new JsString(ToString());
  291. }
  292. }
  293. }
  294. }