TypeConverter.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. using System;
  2. using System.Globalization;
  3. using Jint.Native;
  4. using Jint.Native.Number;
  5. namespace Jint.Runtime
  6. {
  7. public enum Types
  8. {
  9. None,
  10. Undefined,
  11. Null,
  12. Boolean,
  13. String,
  14. Number,
  15. Object
  16. }
  17. public class TypeConverter
  18. {
  19. /// <summary>
  20. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.1
  21. /// </summary>
  22. /// <param name="input"></param>
  23. /// <param name="preferredType"></param>
  24. /// <returns></returns>
  25. public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.None)
  26. {
  27. if (input == Null.Instance || input == Undefined.Instance)
  28. {
  29. return input;
  30. }
  31. if (input.IsPrimitive())
  32. {
  33. return input;
  34. }
  35. return input.AsObject().DefaultValue(preferredType);
  36. }
  37. /// <summary>
  38. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
  39. /// </summary>
  40. /// <param name="o"></param>
  41. /// <returns></returns>
  42. public static JsValue ToBoolean(JsValue o)
  43. {
  44. if (o.IsObject())
  45. {
  46. var p = o.AsObject() as IPrimitiveInstance;
  47. if (p != null)
  48. {
  49. o = p.PrimitiveValue;
  50. }
  51. }
  52. if (o == Undefined.Instance || o == Null.Instance)
  53. {
  54. return JsValue.False;
  55. }
  56. if (o.IsBoolean())
  57. {
  58. return o;
  59. }
  60. if (o.IsNumber())
  61. {
  62. var n = o.AsNumber();
  63. if (n == 0 || double.IsNaN(n))
  64. {
  65. return JsValue.False;
  66. }
  67. else
  68. {
  69. return JsValue.True;
  70. }
  71. }
  72. if (o.IsString())
  73. {
  74. var s = o.AsString();
  75. if (String.IsNullOrEmpty(s))
  76. {
  77. return JsValue.False;
  78. }
  79. else
  80. {
  81. return JsValue.True;
  82. }
  83. }
  84. return JsValue.True;
  85. }
  86. /// <summary>
  87. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.3
  88. /// </summary>
  89. /// <param name="o"></param>
  90. /// <returns></returns>
  91. public static JsValue ToNumber(JsValue o)
  92. {
  93. if (o.IsObject())
  94. {
  95. var p = o.AsObject() as IPrimitiveInstance;
  96. if (p != null)
  97. {
  98. o = p.PrimitiveValue;
  99. }
  100. }
  101. if (o.IsNumber())
  102. {
  103. return o;
  104. }
  105. if (o == Undefined.Instance)
  106. {
  107. return double.NaN;
  108. }
  109. if (o == Null.Instance)
  110. {
  111. return 0;
  112. }
  113. if (o.IsBoolean())
  114. {
  115. return o.AsBoolean() ? 1 : 0;
  116. }
  117. if (o.IsString())
  118. {
  119. double n;
  120. var s = o.AsString().Trim();
  121. if (String.IsNullOrEmpty(s))
  122. {
  123. return 0;
  124. }
  125. if ("+Infinity".Equals(s) || "Infinity".Equals(s))
  126. {
  127. return double.PositiveInfinity;
  128. }
  129. if ("-Infinity".Equals(s))
  130. {
  131. return double.NegativeInfinity;
  132. }
  133. // todo: use a common implementation with JavascriptParser
  134. try
  135. {
  136. if (!s.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
  137. {
  138. var start = s[0];
  139. if (start != '+' && start != '-' && start != '.' && !char.IsDigit(start))
  140. {
  141. return double.NaN;
  142. }
  143. n = Double.Parse(s,
  144. NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign |
  145. NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite |
  146. NumberStyles.AllowExponent, CultureInfo.InvariantCulture);
  147. if (s.StartsWith("-") && n == 0)
  148. {
  149. return -0.0;
  150. }
  151. return n;
  152. }
  153. int i = int.Parse(s.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
  154. return i;
  155. }
  156. catch (OverflowException)
  157. {
  158. return s.StartsWith("-") ? double.NegativeInfinity : double.PositiveInfinity;
  159. }
  160. catch
  161. {
  162. return double.NaN;
  163. }
  164. return double.NaN;
  165. }
  166. return ToNumber(ToPrimitive(o, Types.Number));
  167. }
  168. /// <summary>
  169. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
  170. /// </summary>
  171. /// <param name="o"></param>
  172. /// <returns></returns>
  173. public static JsValue ToInteger(JsValue o)
  174. {
  175. var number = ToNumber(o).AsNumber();
  176. if (double.IsNaN(number))
  177. {
  178. return 0;
  179. }
  180. if (number == 0 || number == double.NegativeInfinity || number == double.PositiveInfinity)
  181. {
  182. return number;
  183. }
  184. return (long)number;
  185. }
  186. /// <summary>
  187. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.5
  188. /// </summary>
  189. /// <param name="o"></param>
  190. /// <returns></returns>
  191. public static int ToInt32(JsValue o)
  192. {
  193. return (int)(uint)ToNumber(o).AsNumber();
  194. }
  195. /// <summary>
  196. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.6
  197. /// </summary>
  198. /// <param name="o"></param>
  199. /// <returns></returns>
  200. public static uint ToUint32(JsValue o)
  201. {
  202. return (uint)ToNumber(o).AsNumber();
  203. }
  204. /// <summary>
  205. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.7
  206. /// </summary>
  207. /// <param name="o"></param>
  208. /// <returns></returns>
  209. public static ushort ToUint16(JsValue o)
  210. {
  211. return (ushort)(uint)ToNumber(o).AsNumber();
  212. }
  213. /// <summary>
  214. /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.8
  215. /// </summary>
  216. /// <param name="o"></param>
  217. /// <returns></returns>
  218. public static JsValue ToString(JsValue o)
  219. {
  220. if (o.IsObject())
  221. {
  222. var p = o.AsObject() as IPrimitiveInstance;
  223. if (p != null)
  224. {
  225. o = p.PrimitiveValue;
  226. }
  227. }
  228. if (o.IsString())
  229. {
  230. return o;
  231. }
  232. if (o == Undefined.Instance)
  233. {
  234. return Undefined.Text;
  235. }
  236. if (o == Null.Instance)
  237. {
  238. return Null.Text;
  239. }
  240. if (o.IsBoolean())
  241. {
  242. return o.AsBoolean() ? "true" : "false";
  243. }
  244. if (o.IsNumber())
  245. {
  246. return NumberPrototype.ToNumberString(o.AsNumber());
  247. }
  248. return ToString(ToPrimitive(o, Types.String));
  249. }
  250. public static JsValue ToObject(Engine engine, JsValue value)
  251. {
  252. if (value.IsObject())
  253. {
  254. return value;
  255. }
  256. if (value == Undefined.Instance)
  257. {
  258. throw new JavaScriptException(engine.TypeError);
  259. }
  260. if (value == Null.Instance)
  261. {
  262. throw new JavaScriptException(engine.TypeError);
  263. }
  264. if (value.IsBoolean())
  265. {
  266. return engine.Boolean.Construct(value.AsBoolean());
  267. }
  268. if (value.IsNumber())
  269. {
  270. return engine.Number.Construct(value.AsNumber());
  271. }
  272. if (value.IsString())
  273. {
  274. return engine.String.Construct(value.AsString());
  275. }
  276. throw new JavaScriptException(engine.TypeError);
  277. }
  278. public static void CheckObjectCoercible(Engine engine, JsValue o)
  279. {
  280. if (o == Undefined.Instance || o == Null.Instance)
  281. {
  282. throw new JavaScriptException(engine.TypeError);
  283. }
  284. }
  285. }
  286. }