GlobalObject.cs 22 KB

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