JsValueExtensions.cs 16 KB

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