GlobalObject.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. using System;
  2. using System.Linq;
  3. using System.Text;
  4. using Jint.Native.Object;
  5. using Jint.Runtime;
  6. using Jint.Runtime.Interop;
  7. namespace Jint.Native.Global
  8. {
  9. public sealed class GlobalObject : ObjectInstance
  10. {
  11. private GlobalObject(Engine engine) : base(engine)
  12. {
  13. }
  14. public static GlobalObject CreateGlobalObject(Engine engine)
  15. {
  16. var global = new GlobalObject(engine) {Prototype = null, Extensible = true};
  17. return global;
  18. }
  19. public void Configure()
  20. {
  21. // this is implementation dependent, and only to pass some unit tests
  22. Prototype = Engine.Object.PrototypeObject;
  23. // Global object properties
  24. FastAddProperty("Object", Engine.Object, true, false, true);
  25. FastAddProperty("Function", Engine.Function, true, false, true);
  26. FastAddProperty("Array", Engine.Array, true, false, true);
  27. FastAddProperty("String", Engine.String, true, false, true);
  28. FastAddProperty("RegExp", Engine.RegExp, true, false, true);
  29. FastAddProperty("Number", Engine.Number, true, false, true);
  30. FastAddProperty("Boolean", Engine.Boolean, true, false, true);
  31. FastAddProperty("Date", Engine.Date, true, false, true);
  32. FastAddProperty("Math", Engine.Math, true, false, true);
  33. FastAddProperty("JSON", Engine.Json, true, false, true);
  34. FastAddProperty("Error", Engine.Error, true, false, true);
  35. FastAddProperty("EvalError", Engine.EvalError, true, false, true);
  36. FastAddProperty("RangeError", Engine.RangeError, true, false, true);
  37. FastAddProperty("ReferenceError", Engine.ReferenceError, true, false, true);
  38. FastAddProperty("SyntaxError", Engine.SyntaxError, true, false, true);
  39. FastAddProperty("TypeError", Engine.TypeError, true, false, true);
  40. FastAddProperty("URIError", Engine.UriError, true, false, true);
  41. FastAddProperty("NaN", double.NaN, false, false, false);
  42. FastAddProperty("Infinity", double.PositiveInfinity, false, false, false);
  43. FastAddProperty("undefined", Undefined.Instance, false, false, false);
  44. // Global object functions
  45. FastAddProperty("parseInt", new ClrFunctionInstance(Engine, ParseInt, 2), true, false, true);
  46. FastAddProperty("parseFloat", new ClrFunctionInstance(Engine, ParseFloat, 1), true, false, true);
  47. FastAddProperty("isNaN", new ClrFunctionInstance(Engine, IsNaN, 1), true, false, true);
  48. FastAddProperty("isFinite", new ClrFunctionInstance(Engine, IsFinite, 1), true, false, true);
  49. FastAddProperty("decodeURI", new ClrFunctionInstance(Engine, DecodeUri, 1), true, false, true);
  50. FastAddProperty("decodeURIComponent", new ClrFunctionInstance(Engine, DecodeUriComponent, 1), true, false, true);
  51. FastAddProperty("encodeURI", new ClrFunctionInstance(Engine, EncodeUri, 1), true, false, true);
  52. FastAddProperty("encodeURIComponent", new ClrFunctionInstance(Engine, EncodeUriComponent, 1), true, false, true);
  53. }
  54. /// <summary>
  55. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.2
  56. /// </summary>
  57. public static JsValue ParseInt(JsValue thisObject, JsValue[] arguments)
  58. {
  59. string inputString = TypeConverter.ToString(arguments.At(0));
  60. var s = inputString.Trim();
  61. var sign = 1;
  62. if (!System.String.IsNullOrEmpty(s))
  63. {
  64. if (s[0] == '-')
  65. {
  66. sign = -1;
  67. }
  68. if (s[0] == '-' || s[0] == '+')
  69. {
  70. s = s.Substring(1);
  71. }
  72. }
  73. var stripPrefix = true;
  74. int radix = arguments.Length > 1 ? TypeConverter.ToInt32(arguments[1]) : 0;
  75. if (radix == 0)
  76. {
  77. if (s.Length >= 2 && s.StartsWith("0x") || s.StartsWith("0X"))
  78. {
  79. radix = 16;
  80. }
  81. else
  82. {
  83. radix = 10;
  84. }
  85. }
  86. else if (radix < 2 || radix > 36)
  87. {
  88. return double.NaN;
  89. }
  90. else if(radix != 16)
  91. {
  92. stripPrefix = false;
  93. }
  94. if (stripPrefix && s.Length >= 2 && s.StartsWith("0x") || s.StartsWith("0X"))
  95. {
  96. s = s.Substring(2);
  97. }
  98. try
  99. {
  100. return sign * Parse(s, radix).AsNumber();
  101. }
  102. catch
  103. {
  104. return double.NaN;
  105. }
  106. }
  107. private static JsValue Parse(string number, int radix)
  108. {
  109. if (number == "")
  110. {
  111. return double.NaN;
  112. }
  113. double result = 0;
  114. double pow = 1;
  115. for (int i = number.Length - 1; i >= 0 ; i--)
  116. {
  117. double index = double.NaN;
  118. char digit = number[i];
  119. if (digit >= '0' && digit <= '9')
  120. {
  121. index = digit - '0';
  122. }
  123. else if (digit >= 'a' && digit <= 'z')
  124. {
  125. index = digit - 'a' + 10;
  126. }
  127. else if (digit >= 'A' && digit <= 'Z')
  128. {
  129. index = digit - 'A' + 10;
  130. }
  131. if (double.IsNaN(index) || index >= radix)
  132. {
  133. return Parse(number.Substring(0, i), radix);
  134. }
  135. result += index*pow;
  136. pow = pow * radix;
  137. }
  138. return result;
  139. }
  140. /// <summary>
  141. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.3
  142. /// </summary>
  143. public static JsValue ParseFloat(JsValue thisObject, JsValue[] arguments)
  144. {
  145. var inputString = TypeConverter.ToString(arguments.At(0));
  146. var trimmedString = inputString.TrimStart();
  147. var sign = 1;
  148. if (trimmedString.Length > 0 )
  149. {
  150. if (trimmedString[0] == '-')
  151. {
  152. sign = -1;
  153. trimmedString = trimmedString.Substring(1);
  154. }
  155. else if (trimmedString[0] == '+')
  156. {
  157. trimmedString = trimmedString.Substring(1);
  158. }
  159. }
  160. if (trimmedString.StartsWith("Infinity"))
  161. {
  162. return sign*double.PositiveInfinity;
  163. }
  164. if (trimmedString.StartsWith("NaN"))
  165. {
  166. return double.NaN;
  167. }
  168. var separator = (char) 0;
  169. bool isNan = true;
  170. decimal number = 0;
  171. var i = 0;
  172. for (; i < trimmedString.Length; i++)
  173. {
  174. var c = trimmedString[i];
  175. if (c == '.')
  176. {
  177. i++;
  178. separator = '.';
  179. break;
  180. }
  181. if(c == 'e' || c == 'E')
  182. {
  183. i++;
  184. separator = 'e';
  185. break;
  186. }
  187. var digit = c - '0';
  188. if (digit >= 0 && digit <= 9)
  189. {
  190. isNan = false;
  191. number = number * 10 + digit;
  192. }
  193. else
  194. {
  195. break;
  196. }
  197. }
  198. decimal pow = 0.1m;
  199. if (separator == '.')
  200. {
  201. for (; i < trimmedString.Length; i++)
  202. {
  203. var c = trimmedString[i];
  204. var digit = c - '0';
  205. if (digit >= 0 && digit <= 9)
  206. {
  207. isNan = false;
  208. number += digit * pow;
  209. pow *= 0.1m;
  210. }
  211. else if (c == 'e' || c == 'E')
  212. {
  213. i++;
  214. separator = 'e';
  215. break;
  216. }
  217. else
  218. {
  219. break;
  220. }
  221. }
  222. }
  223. var exp = 0;
  224. var expSign = 1;
  225. if (separator == 'e')
  226. {
  227. if (i < trimmedString.Length)
  228. {
  229. if (trimmedString[i] == '-')
  230. {
  231. expSign = -1;
  232. i++;
  233. }
  234. else if (trimmedString[i] == '+')
  235. {
  236. i++;
  237. }
  238. }
  239. for (; i < trimmedString.Length; i++)
  240. {
  241. var c = trimmedString[i];
  242. var digit = c - '0';
  243. if (digit >= 0 && digit <= 9)
  244. {
  245. exp = exp * 10 + digit;
  246. }
  247. else
  248. {
  249. break;
  250. }
  251. }
  252. }
  253. if (isNan)
  254. {
  255. return double.NaN;
  256. }
  257. for (var k = 1; k <= exp; k++)
  258. {
  259. if (expSign > 0)
  260. {
  261. number *= 10;
  262. }
  263. else
  264. {
  265. number /= 10;
  266. }
  267. }
  268. return (double) (sign * number);
  269. }
  270. /// <summary>
  271. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.4
  272. /// </summary>
  273. public static JsValue IsNaN(JsValue thisObject, JsValue[] arguments)
  274. {
  275. var x = TypeConverter.ToNumber(arguments.At(0));
  276. return double.IsNaN(x);
  277. }
  278. /// <summary>
  279. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.5
  280. /// </summary>
  281. public static JsValue IsFinite(JsValue thisObject, JsValue[] arguments)
  282. {
  283. if (arguments.Length != 1)
  284. {
  285. return false;
  286. }
  287. var n = TypeConverter.ToNumber(arguments.At(0));
  288. if (double.IsNaN(n) || double.IsInfinity(n))
  289. {
  290. return false;
  291. }
  292. return true;
  293. }
  294. private static readonly char[] UriReserved = {';', '/', '?', ':', '@', '&', '=', '+', '$', ','};
  295. private static readonly char[] UriUnescaped =
  296. {
  297. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  298. 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
  299. 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', '.', '!',
  300. '~', '*', '\'', '(', ')'
  301. };
  302. private const string HexaMap = "0123456789ABCDEF";
  303. private static bool IsValidHexaChar(char c)
  304. {
  305. return
  306. c >= '0' && c <= '9' ||
  307. c >= 'a' && c <= 'f' ||
  308. c >= 'A' && c <= 'F';
  309. }
  310. /// <summary>
  311. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.2
  312. /// </summary>
  313. /// <param name="thisObject"></param>
  314. /// <param name="arguments"></param>
  315. /// <returns></returns>
  316. public JsValue EncodeUri(JsValue thisObject, JsValue[] arguments)
  317. {
  318. var uriString = TypeConverter.ToString(arguments.At(0));
  319. var unescapedUriSet = UriReserved.Concat(UriUnescaped).Concat(new [] {'#'}).ToArray();
  320. return Encode(uriString, unescapedUriSet);
  321. }
  322. /// <summary>
  323. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4
  324. /// </summary>
  325. /// <param name="thisObject"></param>
  326. /// <param name="arguments"></param>
  327. /// <returns></returns>
  328. public JsValue EncodeUriComponent(JsValue thisObject, JsValue[] arguments)
  329. {
  330. var uriString = TypeConverter.ToString(arguments.At(0));
  331. return Encode(uriString, UriUnescaped);
  332. }
  333. private string Encode(string uriString, char[] unescapedUriSet)
  334. {
  335. var strLen = uriString.Length;
  336. var r = new StringBuilder(uriString.Length);
  337. for (var k = 0; k< strLen; k++)
  338. {
  339. var c = uriString[k];
  340. if (System.Array.IndexOf(unescapedUriSet, c) != -1)
  341. {
  342. r.Append(c);
  343. }
  344. else
  345. {
  346. if (c >= 0xDC00 && c <= 0xDBFF)
  347. {
  348. throw new JavaScriptException(Engine.UriError);
  349. }
  350. int v;
  351. if (c < 0xD800 || c > 0xDBFF)
  352. {
  353. v = c;
  354. }
  355. else
  356. {
  357. k++;
  358. if (k == strLen)
  359. {
  360. throw new JavaScriptException(Engine.UriError);
  361. }
  362. var kChar = (int) uriString[k];
  363. if (kChar < 0xDC00 || kChar > 0xDFFF)
  364. {
  365. throw new JavaScriptException(Engine.UriError);
  366. }
  367. v = (c - 0xD800) * 0x400 + (kChar - 0xDC00) + 0x10000;
  368. }
  369. byte[] octets;
  370. if (v >= 0 && v <= 0x007F)
  371. {
  372. // 00000000 0zzzzzzz -> 0zzzzzzz
  373. octets = new[] { (byte)v };
  374. }
  375. else if (v <= 0x07FF)
  376. {
  377. // 00000yyy yyzzzzzz -> 110yyyyy ; 10zzzzzz
  378. octets = new[]
  379. {
  380. (byte)(0xC0 | (v >> 6)),
  381. (byte)(0x80 | (v & 0x3F))
  382. };
  383. }
  384. else if (v <= 0xD7FF)
  385. {
  386. // xxxxyyyy yyzzzzzz -> 1110xxxx; 10yyyyyy; 10zzzzzz
  387. octets = new[]
  388. {
  389. (byte)(0xE0 | (v >> 12)),
  390. (byte)(0x80 | ((v >> 6) & 0x3F)),
  391. (byte)(0x80 | (v & 0x3F))
  392. };
  393. }
  394. else if (v <= 0xDFFF)
  395. {
  396. throw new JavaScriptException(Engine.UriError);
  397. }
  398. else if (v <= 0xFFFF)
  399. {
  400. octets = new[]
  401. {
  402. (byte) (0xE0 | (v >> 12)),
  403. (byte) (0x80 | ((v >> 6) & 0x3F)),
  404. (byte) (0x80 | (v & 0x3F))
  405. };
  406. }
  407. else
  408. {
  409. octets = new[]
  410. {
  411. (byte) (0xF0 | (v >> 18)),
  412. (byte) (0x80 | (v >> 12 & 0x3F)),
  413. (byte) (0x80 | (v >> 6 & 0x3F)),
  414. (byte) (0x80 | (v >> 0 & 0x3F))
  415. };
  416. }
  417. for (var j = 0; j < octets.Length; j++)
  418. {
  419. var jOctet = octets[j];
  420. var x1 = HexaMap[jOctet / 16];
  421. var x2 = HexaMap[jOctet % 16];
  422. r.Append('%').Append(x1).Append(x2);
  423. }
  424. }
  425. }
  426. return r.ToString();
  427. }
  428. public JsValue DecodeUri(JsValue thisObject, JsValue[] arguments)
  429. {
  430. var uriString = TypeConverter.ToString(arguments.At(0));
  431. var reservedUriSet = UriReserved.Concat(new[] { '#' }).ToArray();
  432. return Decode(uriString, reservedUriSet);
  433. }
  434. public JsValue DecodeUriComponent(JsValue thisObject, JsValue[] arguments)
  435. {
  436. var componentString = TypeConverter.ToString(arguments.At(0));
  437. var reservedUriComponentSet = new char[0];
  438. return Decode(componentString, reservedUriComponentSet);
  439. }
  440. public string Decode(string uriString, char[] reservedSet)
  441. {
  442. var strLen = uriString.Length;
  443. var R = new StringBuilder(strLen);
  444. for (var k = 0; k < strLen; k++)
  445. {
  446. var C = uriString[k];
  447. if (C != '%')
  448. {
  449. R.Append(C);
  450. }
  451. else
  452. {
  453. var start = k;
  454. if (k + 2 >= strLen)
  455. {
  456. throw new JavaScriptException(Engine.UriError);
  457. }
  458. if (!IsValidHexaChar(uriString[k + 1]) || !IsValidHexaChar(uriString[k + 2]))
  459. {
  460. throw new JavaScriptException(Engine.UriError);
  461. }
  462. var B = Convert.ToByte(uriString[k + 1].ToString() + uriString[k + 2], 16);
  463. k += 2;
  464. if ((B & 0x80) == 0)
  465. {
  466. C = (char) B;
  467. if (System.Array.IndexOf(reservedSet, C) == -1)
  468. {
  469. R.Append(C);
  470. }
  471. else
  472. {
  473. R.Append(uriString.Substring(start, k - start + 1));
  474. }
  475. }
  476. else
  477. {
  478. var n = 0;
  479. for (; ((B << n) & 0x80) != 0; n++) ;
  480. if (n == 1 || n > 4)
  481. {
  482. throw new JavaScriptException(Engine.UriError);
  483. }
  484. var Octets = new byte[n];
  485. Octets[0] = B;
  486. if (k + (3*(n - 1)) >= strLen)
  487. {
  488. throw new JavaScriptException(Engine.UriError);
  489. }
  490. for(var j=1; j <n; j++)
  491. {
  492. k++;
  493. if (uriString[k] != '%')
  494. {
  495. throw new JavaScriptException(Engine.UriError);
  496. }
  497. if (!IsValidHexaChar(uriString[k + 1]) || !IsValidHexaChar(uriString[k + 2]))
  498. {
  499. throw new JavaScriptException(Engine.UriError);
  500. }
  501. B = Convert.ToByte(uriString[k + 1].ToString() + uriString[k + 2], 16);
  502. // B & 11000000 != 10000000
  503. if ((B & 0xC0) != 0x80)
  504. {
  505. throw new JavaScriptException(Engine.UriError);
  506. }
  507. k += 2;
  508. Octets[j] = B;
  509. }
  510. R.Append(Encoding.UTF8.GetString(Octets, 0, Octets.Length));
  511. }
  512. }
  513. }
  514. return R.ToString();
  515. }
  516. }
  517. }