TypeConverter.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. using System;
  2. using System.Globalization;
  3. using Jint.Native;
  4. using Jint.Native.Object;
  5. namespace Jint.Runtime
  6. {
  7. public class TypeConverter
  8. {
  9. /// <summary>
  10. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.1
  11. /// </summary>
  12. /// <param name="input"></param>
  13. /// <param name="preferredType"></param>
  14. /// <returns></returns>
  15. public static object ToPrimitive(object input, TypeCode preferredType = TypeCode.Empty)
  16. {
  17. if (input == Null.Instance || input == Undefined.Instance)
  18. {
  19. return input;
  20. }
  21. var primitive = input as IPrimitiveType;
  22. if (primitive != null)
  23. {
  24. return primitive.PrimitiveValue;
  25. }
  26. var o = input as ObjectInstance;
  27. if (o == null)
  28. {
  29. return input;
  30. }
  31. return o.DefaultValue(preferredType);
  32. }
  33. public static bool IsPrimitiveValue(object o)
  34. {
  35. return o is string
  36. || o is double
  37. || o is bool
  38. || o is Undefined
  39. || o is Null;
  40. }
  41. /// <summary>
  42. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
  43. /// </summary>
  44. /// <param name="o"></param>
  45. /// <returns></returns>
  46. public static bool ToBoolean(object o)
  47. {
  48. if (o is bool)
  49. {
  50. return (bool)o;
  51. }
  52. if (o == Undefined.Instance || o == Null.Instance)
  53. {
  54. return false;
  55. }
  56. var p = o as IPrimitiveType;
  57. if (p != null)
  58. {
  59. o = ToBoolean(p.PrimitiveValue);
  60. }
  61. if (o is double)
  62. {
  63. var n = (double) o;
  64. if (n == 0 || double.IsNaN(n))
  65. {
  66. return false;
  67. }
  68. else
  69. {
  70. return true;
  71. }
  72. }
  73. var s = o as string;
  74. if (s != null)
  75. {
  76. if (String.IsNullOrEmpty(s))
  77. {
  78. return false;
  79. }
  80. else
  81. {
  82. return true;
  83. }
  84. }
  85. if (o is int)
  86. {
  87. return ToBoolean((double)(int)o);
  88. }
  89. if (o is uint)
  90. {
  91. return ToBoolean((double)(uint)o);
  92. }
  93. return true;
  94. }
  95. /// <summary>
  96. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.3
  97. /// </summary>
  98. /// <param name="o"></param>
  99. /// <returns></returns>
  100. public static double ToNumber(object o)
  101. {
  102. if (o is double)
  103. {
  104. return (double)o;
  105. }
  106. if (o is int)
  107. {
  108. return (int) o;
  109. }
  110. if (o is uint)
  111. {
  112. return (uint)o;
  113. }
  114. if (o is DateTime)
  115. {
  116. return ((DateTime)o).Ticks;
  117. }
  118. if (o == Undefined.Instance)
  119. {
  120. return double.NaN;
  121. }
  122. if (o == Null.Instance)
  123. {
  124. return 0;
  125. }
  126. if (o is bool)
  127. {
  128. return (bool)o ? 1 : 0;
  129. }
  130. var s = o as string;
  131. if (s != null)
  132. {
  133. double n;
  134. s = s.Trim();
  135. if (String.IsNullOrEmpty(s))
  136. {
  137. return 0;
  138. }
  139. if ("+Infinity".Equals(s) || "Infinity".Equals(s))
  140. {
  141. return double.PositiveInfinity;
  142. }
  143. if ("-Infinity".Equals(s))
  144. {
  145. return double.NegativeInfinity;
  146. }
  147. // todo: use a common implementation with JavascriptParser
  148. try
  149. {
  150. if (!s.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
  151. {
  152. var start = s[0];
  153. if (start != '+' && start != '-' && start != '.' && !char.IsDigit(start))
  154. {
  155. return double.NaN;
  156. }
  157. n = Double.Parse(s,
  158. NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign |
  159. NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite |
  160. NumberStyles.AllowExponent, CultureInfo.InvariantCulture);
  161. if (s.StartsWith("-") && n == 0)
  162. {
  163. return -0.0;
  164. }
  165. return n;
  166. }
  167. int i = int.Parse(s.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
  168. return i;
  169. }
  170. catch (OverflowException)
  171. {
  172. return s.StartsWith("-") ? double.NegativeInfinity : double.PositiveInfinity;
  173. }
  174. catch
  175. {
  176. return double.NaN;
  177. }
  178. return double.NaN;
  179. }
  180. return ToNumber(ToPrimitive(o, TypeCode.Double));
  181. }
  182. /// <summary>
  183. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
  184. /// </summary>
  185. /// <param name="o"></param>
  186. /// <returns></returns>
  187. public static double ToInteger(object o)
  188. {
  189. var number = ToNumber(o);
  190. if (double.IsNaN(number))
  191. {
  192. return 0;
  193. }
  194. if (number == 0 || number == double.NegativeInfinity || number == double.PositiveInfinity)
  195. {
  196. return number;
  197. }
  198. return Math.Sign(number)*Math.Floor(Math.Abs(number));
  199. }
  200. /// <summary>
  201. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.5
  202. /// </summary>
  203. /// <param name="o"></param>
  204. /// <returns></returns>
  205. public static int ToInt32(object o)
  206. {
  207. if (o is int)
  208. {
  209. return (int)o;
  210. }
  211. return (int)(uint)ToNumber(o);
  212. }
  213. /// <summary>
  214. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.6
  215. /// </summary>
  216. /// <param name="o"></param>
  217. /// <returns></returns>
  218. public static uint ToUint32(object o)
  219. {
  220. if (o is uint)
  221. {
  222. return (uint)o;
  223. }
  224. return (uint)ToNumber(o);
  225. }
  226. /// <summary>
  227. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.7
  228. /// </summary>
  229. /// <param name="o"></param>
  230. /// <returns></returns>
  231. public static ushort ToUint16(object o)
  232. {
  233. return (ushort)(uint)ToNumber(o);
  234. }
  235. /// <summary>
  236. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.8
  237. /// </summary>
  238. /// <param name="o"></param>
  239. /// <returns></returns>
  240. public static string ToString(object o)
  241. {
  242. var s = o as string;
  243. if (s != null)
  244. {
  245. return s;
  246. }
  247. if (o == Undefined.Instance)
  248. {
  249. return "undefined";
  250. }
  251. if (o == Null.Instance)
  252. {
  253. return "null";
  254. }
  255. var p = o as IPrimitiveType;
  256. if (p != null)
  257. {
  258. o = p.PrimitiveValue;
  259. }
  260. if (o is bool)
  261. {
  262. return (bool) o ? "true" : "false";
  263. }
  264. if (o is double)
  265. {
  266. var n = (double) o;
  267. if (double.IsNaN(n))
  268. {
  269. return "NaN";
  270. }
  271. if (n == double.PositiveInfinity)
  272. {
  273. return "Infinity";
  274. }
  275. if (n == double.NegativeInfinity)
  276. {
  277. return "-Infinity";
  278. }
  279. // todo: fix unit tests in 9.8.1
  280. // var d = -Math.Log10((double)SignificantFraction((decimal)n));
  281. // var i = Math.Log10(Math.Floor(n));
  282. return n.ToString(CultureInfo.InvariantCulture);
  283. }
  284. if (o is int)
  285. {
  286. return ToString((double)(int)o);
  287. }
  288. if (o is uint)
  289. {
  290. return ToString((double)(uint)o);
  291. }
  292. if (o is DateTime)
  293. {
  294. return o.ToString();
  295. }
  296. return ToString(ToPrimitive(o, TypeCode.String));
  297. }
  298. public static ObjectInstance ToObject(Engine engine, object value)
  299. {
  300. var o = value as ObjectInstance;
  301. if (o != null)
  302. {
  303. return o;
  304. }
  305. if (value == Undefined.Instance)
  306. {
  307. throw new JavaScriptException(engine.TypeError);
  308. }
  309. if (value == Null.Instance)
  310. {
  311. throw new JavaScriptException(engine.TypeError);
  312. }
  313. if (value is bool)
  314. {
  315. return engine.Boolean.Construct((bool) value);
  316. }
  317. if (value is int)
  318. {
  319. return engine.Number.Construct((int) value);
  320. }
  321. if (value is uint)
  322. {
  323. return engine.Number.Construct((uint) value);
  324. }
  325. if (value is DateTime)
  326. {
  327. return engine.Date.Construct((DateTime)value);
  328. }
  329. if (value is double)
  330. {
  331. return engine.Number.Construct((double) value);
  332. }
  333. var s = value as string;
  334. if (s != null)
  335. {
  336. return engine.String.Construct(s);
  337. }
  338. throw new JavaScriptException(engine.TypeError);
  339. }
  340. public static TypeCode GetType(object value)
  341. {
  342. if (value == null || value == Undefined.Instance || value == Null.Instance)
  343. {
  344. return TypeCode.Empty;
  345. }
  346. if (value is string)
  347. {
  348. return TypeCode.String;
  349. }
  350. if (value is double || value is int || value is uint || value is ushort)
  351. {
  352. return TypeCode.Double;
  353. }
  354. if (value is bool)
  355. {
  356. return TypeCode.Boolean;
  357. }
  358. return TypeCode.Object;
  359. }
  360. public static void CheckObjectCoercible(Engine engine, object o)
  361. {
  362. if (o == Undefined.Instance || o == Null.Instance)
  363. {
  364. throw new JavaScriptException(engine.TypeError);
  365. }
  366. }
  367. public static Decimal SignificantFraction(Decimal d)
  368. {
  369. var b = decimal.GetBits(d);
  370. return new decimal(1, 0, 0, false, (byte)((b[3] >> 16) & 0x7fff));
  371. }
  372. }
  373. }