JsValue.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. using System;
  2. using System.Diagnostics;
  3. using System.Diagnostics.Contracts;
  4. using Jint.Native.Object;
  5. using Jint.Native.RegExp;
  6. using Jint.Runtime;
  7. using Jint.Runtime.Interop;
  8. namespace Jint.Native
  9. {
  10. [DebuggerTypeProxy(typeof(JsValueDebugView))]
  11. public struct JsValue : IEquatable<JsValue>
  12. {
  13. public static JsValue Undefined = new JsValue(Types.Undefined);
  14. public static JsValue Null = new JsValue(Types.Null);
  15. public static JsValue False = new JsValue(false);
  16. public static JsValue True = new JsValue(true);
  17. public JsValue(bool value)
  18. {
  19. _bool = value;
  20. _double = null;
  21. _object = null;
  22. _string = null;
  23. _type = Types.Boolean;
  24. }
  25. public JsValue(double value)
  26. {
  27. _bool = null;
  28. _double = value;
  29. _object = null;
  30. _string = null;
  31. _type = Types.Number;
  32. }
  33. public JsValue(string value)
  34. {
  35. _bool = null;
  36. _double = null;
  37. _object = null;
  38. _string = value;
  39. _type = Types.String;
  40. }
  41. public JsValue(ObjectInstance value)
  42. {
  43. _bool = null;
  44. _double = null;
  45. _object = value;
  46. _string = null;
  47. _type = Types.Object;
  48. }
  49. private JsValue(Types type)
  50. {
  51. _bool = null;
  52. _double = null;
  53. _object = null;
  54. _string = null;
  55. _type = type;
  56. }
  57. private readonly bool? _bool;
  58. private readonly double? _double;
  59. private readonly ObjectInstance _object;
  60. private readonly string _string;
  61. private readonly Types _type;
  62. [Pure]
  63. public bool IsPrimitive()
  64. {
  65. return _type != Types.Object && _type != Types.None;
  66. }
  67. [Pure]
  68. public bool IsUndefined()
  69. {
  70. return _type == Types.Undefined;
  71. }
  72. [Pure]
  73. public bool IsArray()
  74. {
  75. return IsObject() && AsObject() is Array.ArrayInstance;
  76. }
  77. [Pure]
  78. public bool IsRegExp()
  79. {
  80. return IsObject() && AsObject() is RegExpInstance;
  81. }
  82. [Pure]
  83. public bool IsObject()
  84. {
  85. return _type == Types.Object;
  86. }
  87. [Pure]
  88. public bool IsString()
  89. {
  90. return _type == Types.String;
  91. }
  92. [Pure]
  93. public bool IsNumber()
  94. {
  95. return _type == Types.Number;
  96. }
  97. [Pure]
  98. public bool IsBoolean()
  99. {
  100. return _type == Types.Boolean;
  101. }
  102. [Pure]
  103. public bool IsNull()
  104. {
  105. return _type == Types.Null;
  106. }
  107. [Pure]
  108. public ObjectInstance AsObject()
  109. {
  110. if (_type != Types.Object)
  111. {
  112. throw new ArgumentException("The value is not an object");
  113. }
  114. return _object;
  115. }
  116. [Pure]
  117. public Array.ArrayInstance AsArray()
  118. {
  119. if (!IsArray())
  120. {
  121. throw new ArgumentException("The value is not an array");
  122. }
  123. return AsObject() as Array.ArrayInstance;
  124. }
  125. [Pure]
  126. public T TryCast<T>(Action<JsValue> fail = null) where T: class
  127. {
  128. if (IsObject())
  129. {
  130. var o = AsObject();
  131. var t = o as T;
  132. if (t != null)
  133. {
  134. return t;
  135. }
  136. }
  137. if (fail != null)
  138. {
  139. fail(this);
  140. }
  141. return null;
  142. }
  143. public bool Is<T>()
  144. {
  145. return IsObject() && AsObject() is T;
  146. }
  147. public T As<T>() where T : ObjectInstance
  148. {
  149. return _object as T;
  150. }
  151. [Pure]
  152. public bool AsBoolean()
  153. {
  154. if (_type != Types.Boolean)
  155. {
  156. throw new ArgumentException("The value is not a boolean");
  157. }
  158. if (!_bool.HasValue)
  159. {
  160. throw new ArgumentException("The value is not defined");
  161. }
  162. return _bool.Value;
  163. }
  164. [Pure]
  165. public string AsString()
  166. {
  167. if (_type != Types.String)
  168. {
  169. throw new ArgumentException("The value is not a string");
  170. }
  171. if (_string == null)
  172. {
  173. throw new ArgumentException("The value is not defined");
  174. }
  175. return _string;
  176. }
  177. [Pure]
  178. public double AsNumber()
  179. {
  180. if (_type != Types.Number)
  181. {
  182. throw new ArgumentException("The value is not a number");
  183. }
  184. if (!_double.HasValue)
  185. {
  186. throw new ArgumentException("The value is not defined");
  187. }
  188. return _double.Value;
  189. }
  190. public bool Equals(JsValue other)
  191. {
  192. if (_type != other._type)
  193. {
  194. return false;
  195. }
  196. switch (_type)
  197. {
  198. case Types.None:
  199. return false;
  200. case Types.Undefined:
  201. return true;
  202. case Types.Null:
  203. return true;
  204. case Types.Boolean:
  205. return _bool == other._bool;
  206. case Types.String:
  207. return _string == other._string;
  208. case Types.Number:
  209. return _double == other._double;
  210. case Types.Object:
  211. return _object == other._object;
  212. default:
  213. throw new ArgumentOutOfRangeException();
  214. }
  215. }
  216. public Types Type
  217. {
  218. get { return _type; }
  219. }
  220. /// <summary>
  221. /// Creates a valid <see cref="JsValue"/> instance from any <see cref="Object"/> instance
  222. /// </summary>
  223. /// <param name="engine"></param>
  224. /// <param name="value"></param>
  225. /// <returns></returns>
  226. public static JsValue FromObject(Engine engine, object value)
  227. {
  228. if (value == null)
  229. {
  230. return Null;
  231. }
  232. var typeCode = System.Type.GetTypeCode(value.GetType());
  233. switch (typeCode)
  234. {
  235. case TypeCode.Boolean:
  236. return new JsValue((bool)value);
  237. case TypeCode.Byte:
  238. return new JsValue((double)(byte)value);
  239. case TypeCode.Char:
  240. return new JsValue(value.ToString());
  241. case TypeCode.DateTime:
  242. return engine.Date.Construct((DateTime)value);
  243. case TypeCode.Decimal:
  244. return new JsValue((double)(decimal)value);
  245. case TypeCode.Double:
  246. return new JsValue((double)value);
  247. case TypeCode.Int16:
  248. return new JsValue((double)(Int16)value);
  249. case TypeCode.Int32:
  250. return new JsValue((double)(Int32)value);
  251. case TypeCode.Int64:
  252. return new JsValue((double)(Int64)value);
  253. case TypeCode.SByte:
  254. return new JsValue((double)(SByte)value);
  255. case TypeCode.Single:
  256. return new JsValue((double)(Single)value);
  257. case TypeCode.String:
  258. return new JsValue((string)value);
  259. case TypeCode.UInt16:
  260. return new JsValue((double)(UInt16)value);
  261. case TypeCode.UInt32:
  262. return new JsValue((double)(UInt32)value);
  263. case TypeCode.UInt64:
  264. return new JsValue((double)(UInt64)value);
  265. case TypeCode.Object:
  266. break;
  267. case TypeCode.Empty:
  268. break;
  269. default:
  270. throw new ArgumentOutOfRangeException();
  271. }
  272. var instance = value as ObjectInstance;
  273. if (instance != null)
  274. {
  275. return new JsValue(instance);
  276. }
  277. if (value is JsValue)
  278. {
  279. return (JsValue) value;
  280. }
  281. // if no known type could be guessed, wrap it as an ObjectInstance
  282. return new ObjectWrapper(engine, value);
  283. }
  284. /// <summary>
  285. /// Converts a <see cref="JsValue"/> to its underlying CLR value.
  286. /// </summary>
  287. /// <returns>The underlying CLR value of the <see cref="JsValue"/> instance.</returns>
  288. public object ToObject()
  289. {
  290. switch (_type)
  291. {
  292. case Types.None:
  293. case Types.Undefined:
  294. case Types.Null:
  295. return null;
  296. case Types.Boolean:
  297. return _bool;
  298. case Types.String:
  299. return _string;
  300. case Types.Number:
  301. return _double;
  302. case Types.Object:
  303. return _object;
  304. default:
  305. throw new ArgumentOutOfRangeException();
  306. }
  307. }
  308. public override string ToString()
  309. {
  310. switch (Type)
  311. {
  312. case Types.None:
  313. return "None";
  314. case Types.Undefined:
  315. return "undefined";
  316. case Types.Null:
  317. return "null";
  318. case Types.Boolean:
  319. return _bool.ToString();
  320. case Types.String:
  321. return _string;
  322. case Types.Number:
  323. return _double.ToString();
  324. case Types.Object:
  325. return _object.ToString();
  326. default:
  327. return string.Empty;
  328. }
  329. }
  330. public static bool operator ==(JsValue a, JsValue b)
  331. {
  332. return a.Equals(b);
  333. }
  334. public static bool operator !=(JsValue a, JsValue b)
  335. {
  336. return !a.Equals(b);
  337. }
  338. static public implicit operator JsValue(double value)
  339. {
  340. return new JsValue(value);
  341. }
  342. static public implicit operator JsValue(bool value)
  343. {
  344. return new JsValue(value);
  345. }
  346. static public implicit operator JsValue(string value)
  347. {
  348. return new JsValue(value);
  349. }
  350. static public implicit operator JsValue(ObjectInstance value)
  351. {
  352. return new JsValue(value);
  353. }
  354. internal class JsValueDebugView
  355. {
  356. public string Value;
  357. public JsValueDebugView(JsValue value)
  358. {
  359. switch (value.Type)
  360. {
  361. case Types.None:
  362. Value = "None";
  363. break;
  364. case Types.Undefined:
  365. Value = "undefined";
  366. break;
  367. case Types.Null:
  368. Value = "null";
  369. break;
  370. case Types.Boolean:
  371. Value = value.AsBoolean() + " (bool)";
  372. break;
  373. case Types.String:
  374. Value = value.AsString() + " (string)";
  375. break;
  376. case Types.Number:
  377. Value = value.AsNumber() + " (number)";
  378. break;
  379. case Types.Object:
  380. Value = value.AsObject().GetType().Name;
  381. break;
  382. default:
  383. Value = "Unknown";
  384. break;
  385. }
  386. }
  387. }
  388. public override bool Equals(object obj)
  389. {
  390. if (ReferenceEquals(null, obj)) return false;
  391. return obj is JsValue && Equals((JsValue)obj);
  392. }
  393. public override int GetHashCode()
  394. {
  395. unchecked
  396. {
  397. var hashCode = _bool.GetHashCode();
  398. hashCode = (hashCode * 397) ^ _double.GetHashCode();
  399. hashCode = (hashCode * 397) ^ (_object != null ? _object.GetHashCode() : 0);
  400. hashCode = (hashCode * 397) ^ (_string != null ? _string.GetHashCode() : 0);
  401. hashCode = (hashCode * 397) ^ (int)_type;
  402. return hashCode;
  403. }
  404. }
  405. }
  406. }