JsValueExtensions.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. using System;
  2. using System.Diagnostics.Contracts;
  3. using System.Numerics;
  4. using System.Runtime.CompilerServices;
  5. using Esprima;
  6. using Jint.Native;
  7. using Jint.Native.Array;
  8. using Jint.Native.Date;
  9. using Jint.Native.Function;
  10. using Jint.Native.Object;
  11. using Jint.Native.Promise;
  12. using Jint.Native.RegExp;
  13. using Jint.Native.Symbol;
  14. using Jint.Native.TypedArray;
  15. using Jint.Runtime;
  16. namespace Jint
  17. {
  18. public static class JsValueExtensions
  19. {
  20. [Pure]
  21. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  22. public static bool IsPrimitive(this JsValue value)
  23. {
  24. return (value._type & (InternalTypes.Primitive | InternalTypes.Undefined | InternalTypes.Null)) != 0;
  25. }
  26. [Pure]
  27. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  28. public static bool IsUndefined(this JsValue value)
  29. {
  30. return value._type == InternalTypes.Undefined;
  31. }
  32. [Pure]
  33. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  34. internal static bool IsNullOrUndefined(this JsValue value)
  35. {
  36. return value._type < InternalTypes.Boolean;
  37. }
  38. [Pure]
  39. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  40. public static bool IsDate(this JsValue value)
  41. {
  42. return value is DateInstance;
  43. }
  44. [Pure]
  45. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  46. public static bool IsPromise(this JsValue value)
  47. {
  48. return value is PromiseInstance;
  49. }
  50. [Pure]
  51. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  52. public static bool IsRegExp(this JsValue value)
  53. {
  54. if (!(value is ObjectInstance oi))
  55. {
  56. return false;
  57. }
  58. var matcher = oi.Get(GlobalSymbolRegistry.Match);
  59. if (!matcher.IsUndefined())
  60. {
  61. return TypeConverter.ToBoolean(matcher);
  62. }
  63. return value is RegExpInstance;
  64. }
  65. [Pure]
  66. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  67. public static bool IsObject(this JsValue value)
  68. {
  69. return (value._type & InternalTypes.Object) != 0;
  70. }
  71. [Pure]
  72. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  73. public static bool IsString(this JsValue value)
  74. {
  75. return (value._type & InternalTypes.String) != 0;
  76. }
  77. [Pure]
  78. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  79. public static bool IsNumber(this JsValue value)
  80. {
  81. return (value._type & (InternalTypes.Number | InternalTypes.Integer)) != 0;
  82. }
  83. [Pure]
  84. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  85. public static bool IsBigInt(this JsValue value)
  86. {
  87. return (value._type & InternalTypes.BigInt) != 0;
  88. }
  89. [Pure]
  90. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  91. internal static bool IsInteger(this JsValue value)
  92. {
  93. return value._type == InternalTypes.Integer;
  94. }
  95. [Pure]
  96. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  97. public static bool IsBoolean(this JsValue value)
  98. {
  99. return value._type == InternalTypes.Boolean;
  100. }
  101. [Pure]
  102. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  103. public static bool IsNull(this JsValue value)
  104. {
  105. return value._type == InternalTypes.Null;
  106. }
  107. [Pure]
  108. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  109. public static bool IsSymbol(this JsValue value)
  110. {
  111. return value._type == InternalTypes.Symbol;
  112. }
  113. [Pure]
  114. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  115. public static DateInstance AsDate(this JsValue value)
  116. {
  117. if (!value.IsDate())
  118. {
  119. ExceptionHelper.ThrowArgumentException("The value is not a date");
  120. }
  121. return value as DateInstance;
  122. }
  123. [Pure]
  124. public static RegExpInstance AsRegExp(this JsValue value)
  125. {
  126. if (!value.IsRegExp())
  127. {
  128. ExceptionHelper.ThrowArgumentException("The value is not a regex");
  129. }
  130. return value as RegExpInstance;
  131. }
  132. [Pure]
  133. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  134. public static ObjectInstance AsObject(this JsValue value)
  135. {
  136. if (!value.IsObject())
  137. {
  138. ExceptionHelper.ThrowArgumentException("The value is not an object");
  139. }
  140. return value as ObjectInstance;
  141. }
  142. [Pure]
  143. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  144. public static TInstance AsInstance<TInstance>(this JsValue value) where TInstance : class
  145. {
  146. if (!value.IsObject())
  147. {
  148. ExceptionHelper.ThrowArgumentException("The value is not an object");
  149. }
  150. return value as TInstance;
  151. }
  152. [Pure]
  153. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  154. public static ArrayInstance AsArray(this JsValue value)
  155. {
  156. if (!value.IsArray())
  157. {
  158. ExceptionHelper.ThrowArgumentException("The value is not an array");
  159. }
  160. return value as ArrayInstance;
  161. }
  162. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  163. public static bool AsBoolean(this JsValue value)
  164. {
  165. if (value._type != InternalTypes.Boolean)
  166. {
  167. ThrowWrongTypeException(value, "boolean");
  168. }
  169. return ((JsBoolean) value)._value;
  170. }
  171. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  172. public static double AsNumber(this JsValue value)
  173. {
  174. if (!value.IsNumber())
  175. {
  176. ThrowWrongTypeException(value, "number");
  177. }
  178. return ((JsNumber) value)._value;
  179. }
  180. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  181. internal static int AsInteger(this JsValue value)
  182. {
  183. return (int) ((JsNumber) value)._value;
  184. }
  185. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  186. internal static BigInteger AsBigInt(this JsValue value)
  187. {
  188. return ((JsBigInt) value)._value;
  189. }
  190. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  191. public static string AsString(this JsValue value)
  192. {
  193. if (!value.IsString())
  194. {
  195. ThrowWrongTypeException(value, "string");
  196. }
  197. return value.ToString();
  198. }
  199. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  200. public static bool IsUint8Array(this JsValue value)
  201. {
  202. return value is TypedArrayInstance { _arrayElementType: TypedArrayElementType.Uint8 };
  203. }
  204. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  205. public static byte[] AsUint8Array(this JsValue value)
  206. {
  207. if (!value.IsUint8Array())
  208. {
  209. ThrowWrongTypeException(value, "Uint8Array");
  210. }
  211. return ((TypedArrayInstance) value).ToNativeArray<byte>();
  212. }
  213. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  214. public static bool IsUint8ClampedArray(this JsValue value)
  215. {
  216. return value is TypedArrayInstance { _arrayElementType: TypedArrayElementType.Uint8C };
  217. }
  218. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  219. public static byte[] AsUint8ClampedArray(this JsValue value)
  220. {
  221. if (!value.IsUint8ClampedArray())
  222. {
  223. ThrowWrongTypeException(value, "Uint8ClampedArray");
  224. }
  225. return ((TypedArrayInstance) value).ToNativeArray<byte>();
  226. }
  227. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  228. public static bool IsInt8Array(this JsValue value)
  229. {
  230. return value is TypedArrayInstance { _arrayElementType: TypedArrayElementType.Int8 };
  231. }
  232. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  233. public static sbyte[] AsInt8Array(this JsValue value)
  234. {
  235. if (!value.IsInt8Array())
  236. {
  237. ThrowWrongTypeException(value, "Int8Array");
  238. }
  239. return ((TypedArrayInstance) value).ToNativeArray<sbyte>();
  240. }
  241. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  242. public static bool IsInt16Array(this JsValue value)
  243. {
  244. return value is TypedArrayInstance { _arrayElementType: TypedArrayElementType.Int16 };
  245. }
  246. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  247. public static short[] AsInt16Array(this JsValue value)
  248. {
  249. if (!value.IsInt16Array())
  250. {
  251. ThrowWrongTypeException(value, "Int16Array");
  252. }
  253. return ((TypedArrayInstance) value).ToNativeArray<short>();
  254. }
  255. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  256. public static bool IsUint16Array(this JsValue value)
  257. {
  258. return value is TypedArrayInstance { _arrayElementType: TypedArrayElementType.Uint16 };
  259. }
  260. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  261. public static ushort[] AsUint16Array(this JsValue value)
  262. {
  263. if (!value.IsUint16Array())
  264. {
  265. ThrowWrongTypeException(value, "Uint16Array");
  266. }
  267. return ((TypedArrayInstance) value).ToNativeArray<ushort>();
  268. }
  269. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  270. public static bool IsInt32Array(this JsValue value)
  271. {
  272. return value is TypedArrayInstance { _arrayElementType: TypedArrayElementType.Int32 };
  273. }
  274. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  275. public static int[] AsInt32Array(this JsValue value)
  276. {
  277. if (!value.IsInt32Array())
  278. {
  279. ThrowWrongTypeException(value, "Int32Array");
  280. }
  281. return ((TypedArrayInstance) value).ToNativeArray<int>();
  282. }
  283. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  284. public static bool IsUint32Array(this JsValue value)
  285. {
  286. return value is TypedArrayInstance { _arrayElementType: TypedArrayElementType.Uint32 };
  287. }
  288. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  289. public static uint[] AsUint32Array(this JsValue value)
  290. {
  291. if (!value.IsUint32Array())
  292. {
  293. ThrowWrongTypeException(value, "Uint32Array");
  294. }
  295. return ((TypedArrayInstance) value).ToNativeArray<uint>();
  296. }
  297. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  298. public static bool IsBigInt64Array(this JsValue value)
  299. {
  300. return value is TypedArrayInstance { _arrayElementType: TypedArrayElementType.BigInt64 };
  301. }
  302. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  303. public static long[] AsBigInt64Array(this JsValue value)
  304. {
  305. if (!value.IsBigInt64Array())
  306. {
  307. ThrowWrongTypeException(value, "BigInt64Array");
  308. }
  309. return ((TypedArrayInstance) value).ToNativeArray<long>();
  310. }
  311. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  312. public static bool IsBigUint64Array(this JsValue value)
  313. {
  314. return value is TypedArrayInstance { _arrayElementType: TypedArrayElementType.BigUint64 };
  315. }
  316. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  317. public static ulong[] AsBigUint64Array(this JsValue value)
  318. {
  319. if (!value.IsBigUint64Array())
  320. {
  321. ThrowWrongTypeException(value, "BigUint64Array");
  322. }
  323. return ((TypedArrayInstance) value).ToNativeArray<ulong>();
  324. }
  325. [Pure]
  326. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  327. public static T TryCast<T>(this JsValue value) where T : class
  328. {
  329. return value as T;
  330. }
  331. [Pure]
  332. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  333. public static T TryCast<T>(this JsValue value, Action<JsValue> fail) where T : class
  334. {
  335. if (value is T o)
  336. {
  337. return o;
  338. }
  339. fail.Invoke(value);
  340. return null;
  341. }
  342. [Pure]
  343. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  344. public static T As<T>(this JsValue value) where T : ObjectInstance
  345. {
  346. if (value.IsObject())
  347. {
  348. return value as T;
  349. }
  350. return null;
  351. }
  352. [Pure]
  353. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  354. public static FunctionInstance AsFunctionInstance(this JsValue value)
  355. {
  356. if (value is not FunctionInstance instance)
  357. {
  358. ThrowWrongTypeException(value, "FunctionInstance");
  359. return null;
  360. }
  361. return instance;
  362. }
  363. [Pure]
  364. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  365. public static JsValue Call(this JsValue value, params JsValue[] arguments)
  366. {
  367. if (!value.IsCallable)
  368. {
  369. ExceptionHelper.ThrowArgumentException(value + " is not callable");
  370. }
  371. return ((ICallable) value).Call(JsValue.Undefined, arguments);
  372. }
  373. /// <summary>
  374. /// If the value is a Promise
  375. /// 1. If "Fulfilled" returns the value it was fulfilled with
  376. /// 2. If "Rejected" throws "PromiseRejectedException" with the rejection reason
  377. /// 3. If "Pending" throws "InvalidOperationException". Should be called only in "Settled" state
  378. /// Else
  379. /// returns the value intact
  380. /// </summary>
  381. /// <param name="value">value to unwrap</param>
  382. /// <returns>inner value if Promise the value itself otherwise</returns>
  383. public static JsValue UnwrapIfPromise(this JsValue value)
  384. {
  385. if (value is PromiseInstance promise)
  386. {
  387. switch (promise.State)
  388. {
  389. case PromiseState.Pending:
  390. ExceptionHelper.ThrowInvalidOperationException("'UnwrapIfPromise' called before Promise was settled");
  391. return null;
  392. case PromiseState.Fulfilled:
  393. return promise.Value;
  394. case PromiseState.Rejected:
  395. ExceptionHelper.ThrowPromiseRejectedException(promise.Value);
  396. return null;
  397. default:
  398. ExceptionHelper.ThrowArgumentOutOfRangeException();
  399. return null;
  400. }
  401. }
  402. return value;
  403. }
  404. [MethodImpl(MethodImplOptions.NoInlining)]
  405. private static void ThrowWrongTypeException(JsValue value, string expectedType)
  406. {
  407. ExceptionHelper.ThrowArgumentException($"Expected {expectedType} but got {value._type}");
  408. }
  409. internal static BigInteger ToBigInteger(this JsValue value, Engine engine)
  410. {
  411. try
  412. {
  413. return TypeConverter.ToBigInt(value);
  414. }
  415. catch (ParserException ex)
  416. {
  417. ExceptionHelper.ThrowSyntaxError(engine.Realm, ex.Message);
  418. return default;
  419. }
  420. }
  421. }
  422. }