GlobalObject.cs 20 KB

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