GlobalObject.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Runtime.CompilerServices;
  6. using System.Text;
  7. using Jint.Collections;
  8. using Jint.Native.Object;
  9. using Jint.Native.String;
  10. using Jint.Runtime;
  11. using Jint.Runtime.Descriptors;
  12. using Jint.Runtime.Descriptors.Specialized;
  13. using Jint.Runtime.Interop;
  14. namespace Jint.Native.Global
  15. {
  16. public sealed class GlobalObject : ObjectInstance
  17. {
  18. private readonly Realm _realm;
  19. private readonly StringBuilder _stringBuilder = new();
  20. internal GlobalObject(
  21. Engine engine,
  22. Realm realm) : base(engine)
  23. {
  24. _realm = realm;
  25. // this is implementation dependent, and only to pass some unit tests
  26. _prototype = realm.Intrinsics.Object.PrototypeObject;
  27. }
  28. protected override void Initialize()
  29. {
  30. const PropertyFlag lengthFlags = PropertyFlag.Configurable;
  31. const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
  32. var properties = new PropertyDictionary(54, checkExistingKeys: false)
  33. {
  34. ["Object"] = new PropertyDescriptor(_realm.Intrinsics.Object, propertyFlags),
  35. ["Function"] = new PropertyDescriptor(_realm.Intrinsics.Function, propertyFlags),
  36. ["Symbol"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Symbol, propertyFlags),
  37. ["Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Array, propertyFlags),
  38. ["ArrayBuffer"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.ArrayBuffer, propertyFlags),
  39. ["DataView"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.DataView, propertyFlags),
  40. ["TypedArray"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.TypedArray, propertyFlags),
  41. ["Int8Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Int8Array, propertyFlags),
  42. ["Uint8Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Uint8Array, propertyFlags),
  43. ["Uint8ClampedArray"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Uint8ClampedArray, propertyFlags),
  44. ["Int16Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Int16Array, propertyFlags),
  45. ["Uint16Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Uint16Array, propertyFlags),
  46. ["Int32Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Int32Array, propertyFlags),
  47. ["Uint32Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Uint32Array, propertyFlags),
  48. ["BigInt64Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.BigInt64Array, propertyFlags),
  49. ["BigUint64Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.BigUint64Array, propertyFlags),
  50. ["Float32Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Float32Array, propertyFlags),
  51. ["Float64Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Float64Array, propertyFlags),
  52. ["Map"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Map, propertyFlags),
  53. ["Set"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Set, propertyFlags),
  54. ["WeakMap"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.WeakMap, propertyFlags),
  55. ["WeakSet"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.WeakSet, propertyFlags),
  56. ["Promise"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Promise, propertyFlags),
  57. ["String"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.String, propertyFlags),
  58. ["RegExp"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.RegExp, propertyFlags),
  59. ["Number"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Number, propertyFlags),
  60. ["Boolean"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Boolean, propertyFlags),
  61. ["Date"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Date, propertyFlags),
  62. ["Math"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Math, propertyFlags),
  63. ["JSON"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Json, propertyFlags),
  64. ["Error"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Error, propertyFlags),
  65. ["EvalError"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.EvalError, propertyFlags),
  66. ["Proxy"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Proxy, propertyFlags),
  67. ["RangeError"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.RangeError, propertyFlags),
  68. ["ReferenceError"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.ReferenceError, propertyFlags),
  69. ["Reflect"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Reflect, propertyFlags),
  70. ["SyntaxError"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.SyntaxError, propertyFlags),
  71. ["TypeError"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.TypeError, propertyFlags),
  72. ["URIError"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.UriError, propertyFlags),
  73. ["NaN"] = new PropertyDescriptor(double.NaN, PropertyFlag.None),
  74. ["Infinity"] = new PropertyDescriptor(double.PositiveInfinity, PropertyFlag.None),
  75. ["undefined"] = new PropertyDescriptor(Undefined, PropertyFlag.None),
  76. ["parseInt"] = new LazyPropertyDescriptor(this, static state => new ClrFunctionInstance(((GlobalObject) state)._engine, "parseInt", ParseInt, 2, lengthFlags), propertyFlags),
  77. ["parseFloat"] = new LazyPropertyDescriptor(this, static state => new ClrFunctionInstance(((GlobalObject) state)._engine, "parseFloat", ParseFloat, 1, lengthFlags), propertyFlags),
  78. ["isNaN"] = new LazyPropertyDescriptor(this, static state => new ClrFunctionInstance(((GlobalObject) state)._engine, "isNaN", IsNaN, 1, lengthFlags), propertyFlags),
  79. ["isFinite"] = new LazyPropertyDescriptor(this, static state => new ClrFunctionInstance(((GlobalObject) state)._engine, "isFinite", IsFinite, 1, lengthFlags), propertyFlags),
  80. ["decodeURI"] = new LazyPropertyDescriptor(this, static state =>
  81. {
  82. var global = (GlobalObject) state;
  83. return new ClrFunctionInstance(global._engine, "decodeURI", global.DecodeUri, 1, lengthFlags);
  84. }, propertyFlags),
  85. ["decodeURIComponent"] = new LazyPropertyDescriptor(this, static state =>
  86. {
  87. var global = (GlobalObject) state;
  88. return new ClrFunctionInstance(global._engine, "decodeURIComponent", global.DecodeUriComponent, 1, lengthFlags);
  89. }, propertyFlags),
  90. ["encodeURI"] = new LazyPropertyDescriptor(this, static state =>
  91. {
  92. var global = (GlobalObject) state;
  93. return new ClrFunctionInstance(global._engine, "encodeURI", global.EncodeUri, 1, lengthFlags);
  94. }, propertyFlags),
  95. ["encodeURIComponent"] = new LazyPropertyDescriptor(this, static state =>
  96. {
  97. var global = (GlobalObject) state;
  98. return new ClrFunctionInstance(global._engine, "encodeURIComponent", global.EncodeUriComponent, 1, lengthFlags);
  99. }, propertyFlags),
  100. ["escape"] = new LazyPropertyDescriptor(this, static state =>
  101. {
  102. var global = (GlobalObject) state;
  103. return new ClrFunctionInstance(global._engine, "escape", global.Escape, 1, lengthFlags);
  104. }, propertyFlags),
  105. ["unescape"] = new LazyPropertyDescriptor(this, static state =>
  106. {
  107. var global = (GlobalObject) state;
  108. return new ClrFunctionInstance(global._engine, "unescape", global.Unescape, 1, lengthFlags);
  109. }, propertyFlags),
  110. ["globalThis"] = new PropertyDescriptor(this, propertyFlags),
  111. ["eval"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Eval, PropertyFlag.Configurable | PropertyFlag.Writable),
  112. // toString is not mentioned or actually required in spec, but some tests rely on it
  113. ["toString"] = new LazyPropertyDescriptor(this, static state =>
  114. {
  115. var global = (GlobalObject) state;
  116. return new ClrFunctionInstance(global._engine, "toString", global.ToStringString, 1);
  117. }, propertyFlags)
  118. };
  119. SetProperties(properties);
  120. }
  121. private JsValue ToStringString(JsValue thisObj, JsValue[] arguments)
  122. {
  123. return _realm.Intrinsics.Object.PrototypeObject.ToObjectString(thisObj, Arguments.Empty);
  124. }
  125. /// <summary>
  126. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.2
  127. /// </summary>
  128. public static JsValue ParseInt(JsValue thisObject, JsValue[] arguments)
  129. {
  130. string inputString = TypeConverter.ToString(arguments.At(0));
  131. var s = StringPrototype.TrimEx(inputString);
  132. var sign = 1;
  133. if (!System.String.IsNullOrEmpty(s))
  134. {
  135. if (s[0] == '-')
  136. {
  137. sign = -1;
  138. }
  139. if (s[0] == '-' || s[0] == '+')
  140. {
  141. s = s.Substring(1);
  142. }
  143. }
  144. var stripPrefix = true;
  145. int radix = arguments.Length > 1 ? TypeConverter.ToInt32(arguments[1]) : 0;
  146. if (radix == 0)
  147. {
  148. if (s.Length >= 2 && s.StartsWith("0x") || s.StartsWith("0X"))
  149. {
  150. radix = 16;
  151. }
  152. else
  153. {
  154. radix = 10;
  155. }
  156. }
  157. else if (radix < 2 || radix > 36)
  158. {
  159. return JsNumber.DoubleNaN;
  160. }
  161. else if (radix != 16)
  162. {
  163. stripPrefix = false;
  164. }
  165. if (stripPrefix && s.Length >= 2 && s.StartsWith("0x") || s.StartsWith("0X"))
  166. {
  167. s = s.Substring(2);
  168. }
  169. try
  170. {
  171. return sign * Parse(s, radix);
  172. }
  173. catch
  174. {
  175. return JsNumber.DoubleNaN;
  176. }
  177. }
  178. private static double Parse(string number, int radix)
  179. {
  180. if (number == "")
  181. {
  182. return double.NaN;
  183. }
  184. double result = 0;
  185. double pow = 1;
  186. for (int i = number.Length - 1; i >= 0; i--)
  187. {
  188. double index = double.NaN;
  189. char digit = number[i];
  190. if (digit >= '0' && digit <= '9')
  191. {
  192. index = digit - '0';
  193. }
  194. else if (digit >= 'a' && digit <= 'z')
  195. {
  196. index = digit - 'a' + 10;
  197. }
  198. else if (digit >= 'A' && digit <= 'Z')
  199. {
  200. index = digit - 'A' + 10;
  201. }
  202. if (double.IsNaN(index) || index >= radix)
  203. {
  204. return Parse(number.Substring(0, i), radix);
  205. }
  206. result += index * pow;
  207. pow = pow * radix;
  208. }
  209. return result;
  210. }
  211. /// <summary>
  212. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.3
  213. /// </summary>
  214. public static JsValue ParseFloat(JsValue thisObject, JsValue[] arguments)
  215. {
  216. var inputString = TypeConverter.ToString(arguments.At(0));
  217. var trimmedString = StringPrototype.TrimStartEx(inputString);
  218. var sign = 1;
  219. if (trimmedString.Length > 0)
  220. {
  221. if (trimmedString[0] == '-')
  222. {
  223. sign = -1;
  224. trimmedString = trimmedString.Substring(1);
  225. }
  226. else if (trimmedString[0] == '+')
  227. {
  228. trimmedString = trimmedString.Substring(1);
  229. }
  230. }
  231. if (trimmedString.StartsWith("Infinity"))
  232. {
  233. return sign * double.PositiveInfinity;
  234. }
  235. if (trimmedString.StartsWith("NaN"))
  236. {
  237. return JsNumber.DoubleNaN;
  238. }
  239. var separator = (char)0;
  240. bool isNan = true;
  241. decimal number = 0;
  242. var i = 0;
  243. for (; i < trimmedString.Length; i++)
  244. {
  245. var c = trimmedString[i];
  246. if (c == '.')
  247. {
  248. i++;
  249. separator = '.';
  250. break;
  251. }
  252. if (c == 'e' || c == 'E')
  253. {
  254. i++;
  255. separator = 'e';
  256. break;
  257. }
  258. var digit = c - '0';
  259. if (digit >= 0 && digit <= 9)
  260. {
  261. isNan = false;
  262. number = number * 10 + digit;
  263. }
  264. else
  265. {
  266. break;
  267. }
  268. }
  269. decimal pow = 0.1m;
  270. if (separator == '.')
  271. {
  272. for (; i < trimmedString.Length; i++)
  273. {
  274. var c = trimmedString[i];
  275. var digit = c - '0';
  276. if (digit >= 0 && digit <= 9)
  277. {
  278. isNan = false;
  279. number += digit * pow;
  280. pow *= 0.1m;
  281. }
  282. else if (c == 'e' || c == 'E')
  283. {
  284. i++;
  285. separator = 'e';
  286. break;
  287. }
  288. else
  289. {
  290. break;
  291. }
  292. }
  293. }
  294. var exp = 0;
  295. var expSign = 1;
  296. if (separator == 'e')
  297. {
  298. if (i < trimmedString.Length)
  299. {
  300. if (trimmedString[i] == '-')
  301. {
  302. expSign = -1;
  303. i++;
  304. }
  305. else if (trimmedString[i] == '+')
  306. {
  307. i++;
  308. }
  309. }
  310. for (; i < trimmedString.Length; i++)
  311. {
  312. var c = trimmedString[i];
  313. var digit = c - '0';
  314. if (digit >= 0 && digit <= 9)
  315. {
  316. exp = exp * 10 + digit;
  317. }
  318. else
  319. {
  320. break;
  321. }
  322. }
  323. }
  324. if (isNan)
  325. {
  326. return JsNumber.DoubleNaN;
  327. }
  328. for (var k = 1; k <= exp; k++)
  329. {
  330. if (expSign > 0)
  331. {
  332. number *= 10;
  333. }
  334. else
  335. {
  336. number /= 10;
  337. }
  338. }
  339. return (double)(sign * number);
  340. }
  341. /// <summary>
  342. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.4
  343. /// </summary>
  344. public static JsValue IsNaN(JsValue thisObject, JsValue[] arguments)
  345. {
  346. var x = TypeConverter.ToNumber(arguments.At(0));
  347. return double.IsNaN(x);
  348. }
  349. /// <summary>
  350. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.5
  351. /// </summary>
  352. public static JsValue IsFinite(JsValue thisObject, JsValue[] arguments)
  353. {
  354. if (arguments.Length != 1)
  355. {
  356. return false;
  357. }
  358. var n = TypeConverter.ToNumber(arguments.At(0));
  359. if (double.IsNaN(n) || double.IsInfinity(n))
  360. {
  361. return false;
  362. }
  363. return true;
  364. }
  365. private static readonly HashSet<char> UriReserved = new HashSet<char>
  366. {
  367. ';', '/', '?', ':', '@', '&', '=', '+', '$', ','
  368. };
  369. private static readonly HashSet<char> UriUnescaped = new HashSet<char>
  370. {
  371. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  372. 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
  373. 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', '.', '!',
  374. '~', '*', '\'', '(', ')'
  375. };
  376. private static readonly HashSet<char> UnescapedUriSet = new HashSet<char>(UriReserved.Concat(UriUnescaped).Concat(new[] { '#' }));
  377. private static readonly HashSet<char> ReservedUriSet = new HashSet<char>(UriReserved.Concat(new[] { '#' }));
  378. private const string HexaMap = "0123456789ABCDEF";
  379. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  380. private static bool IsValidHexaChar(char c) => Uri.IsHexDigit(c);
  381. /// <summary>
  382. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.2
  383. /// </summary>
  384. /// <param name="thisObject"></param>
  385. /// <param name="arguments"></param>
  386. /// <returns></returns>
  387. public JsValue EncodeUri(JsValue thisObject, JsValue[] arguments)
  388. {
  389. var uriString = TypeConverter.ToString(arguments.At(0));
  390. return Encode(uriString, UnescapedUriSet);
  391. }
  392. /// <summary>
  393. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4
  394. /// </summary>
  395. /// <param name="thisObject"></param>
  396. /// <param name="arguments"></param>
  397. /// <returns></returns>
  398. public JsValue EncodeUriComponent(JsValue thisObject, JsValue[] arguments)
  399. {
  400. var uriString = TypeConverter.ToString(arguments.At(0));
  401. return Encode(uriString, UriUnescaped);
  402. }
  403. private string Encode(string uriString, HashSet<char> unescapedUriSet)
  404. {
  405. var strLen = uriString.Length;
  406. _stringBuilder.EnsureCapacity(uriString.Length);
  407. _stringBuilder.Clear();
  408. for (var k = 0; k < strLen; k++)
  409. {
  410. var c = uriString[k];
  411. if (unescapedUriSet != null && unescapedUriSet.Contains(c))
  412. {
  413. _stringBuilder.Append(c);
  414. }
  415. else
  416. {
  417. if (c >= 0xDC00 && c <= 0xDBFF)
  418. {
  419. ExceptionHelper.ThrowUriError(_realm);
  420. }
  421. int v;
  422. if (c < 0xD800 || c > 0xDBFF)
  423. {
  424. v = c;
  425. }
  426. else
  427. {
  428. k++;
  429. if (k == strLen)
  430. {
  431. ExceptionHelper.ThrowUriError(_realm);
  432. }
  433. var kChar = (int)uriString[k];
  434. if (kChar < 0xDC00 || kChar > 0xDFFF)
  435. {
  436. ExceptionHelper.ThrowUriError(_realm);
  437. }
  438. v = (c - 0xD800) * 0x400 + (kChar - 0xDC00) + 0x10000;
  439. }
  440. byte[] octets = System.Array.Empty<byte>();
  441. if (v >= 0 && v <= 0x007F)
  442. {
  443. // 00000000 0zzzzzzz -> 0zzzzzzz
  444. octets = new[] { (byte)v };
  445. }
  446. else if (v <= 0x07FF)
  447. {
  448. // 00000yyy yyzzzzzz -> 110yyyyy ; 10zzzzzz
  449. octets = new[]
  450. {
  451. (byte)(0xC0 | (v >> 6)),
  452. (byte)(0x80 | (v & 0x3F))
  453. };
  454. }
  455. else if (v <= 0xD7FF)
  456. {
  457. // xxxxyyyy yyzzzzzz -> 1110xxxx; 10yyyyyy; 10zzzzzz
  458. octets = new[]
  459. {
  460. (byte)(0xE0 | (v >> 12)),
  461. (byte)(0x80 | ((v >> 6) & 0x3F)),
  462. (byte)(0x80 | (v & 0x3F))
  463. };
  464. }
  465. else if (v <= 0xDFFF)
  466. {
  467. ExceptionHelper.ThrowUriError(_realm);
  468. }
  469. else if (v <= 0xFFFF)
  470. {
  471. octets = new[]
  472. {
  473. (byte) (0xE0 | (v >> 12)),
  474. (byte) (0x80 | ((v >> 6) & 0x3F)),
  475. (byte) (0x80 | (v & 0x3F))
  476. };
  477. }
  478. else
  479. {
  480. octets = new[]
  481. {
  482. (byte) (0xF0 | (v >> 18)),
  483. (byte) (0x80 | (v >> 12 & 0x3F)),
  484. (byte) (0x80 | (v >> 6 & 0x3F)),
  485. (byte) (0x80 | (v >> 0 & 0x3F))
  486. };
  487. }
  488. for (var j = 0; j < octets.Length; j++)
  489. {
  490. var jOctet = octets[j];
  491. var x1 = HexaMap[jOctet / 16];
  492. var x2 = HexaMap[jOctet % 16];
  493. _stringBuilder.Append('%').Append(x1).Append(x2);
  494. }
  495. }
  496. }
  497. return _stringBuilder.ToString();
  498. }
  499. public JsValue DecodeUri(JsValue thisObject, JsValue[] arguments)
  500. {
  501. var uriString = TypeConverter.ToString(arguments.At(0));
  502. return Decode(uriString, ReservedUriSet);
  503. }
  504. public JsValue DecodeUriComponent(JsValue thisObject, JsValue[] arguments)
  505. {
  506. var componentString = TypeConverter.ToString(arguments.At(0));
  507. return Decode(componentString, null);
  508. }
  509. private string Decode(string uriString, HashSet<char> reservedSet)
  510. {
  511. var strLen = uriString.Length;
  512. _stringBuilder.EnsureCapacity(strLen);
  513. _stringBuilder.Clear();
  514. var octets = System.Array.Empty<byte>();
  515. for (var k = 0; k < strLen; k++)
  516. {
  517. var C = uriString[k];
  518. if (C != '%')
  519. {
  520. _stringBuilder.Append(C);
  521. }
  522. else
  523. {
  524. var start = k;
  525. if (k + 2 >= strLen)
  526. {
  527. ExceptionHelper.ThrowUriError(_realm);
  528. }
  529. if (!IsValidHexaChar(uriString[k + 1]) || !IsValidHexaChar(uriString[k + 2]))
  530. {
  531. ExceptionHelper.ThrowUriError(_realm);
  532. }
  533. var B = Convert.ToByte(uriString[k + 1].ToString() + uriString[k + 2], 16);
  534. k += 2;
  535. if ((B & 0x80) == 0)
  536. {
  537. C = (char)B;
  538. if (reservedSet == null || !reservedSet.Contains(C))
  539. {
  540. _stringBuilder.Append(C);
  541. }
  542. else
  543. {
  544. _stringBuilder.Append(uriString, start, k - start + 1);
  545. }
  546. }
  547. else
  548. {
  549. var n = 0;
  550. for (; ((B << n) & 0x80) != 0; n++) ;
  551. if (n == 1 || n > 4)
  552. {
  553. ExceptionHelper.ThrowUriError(_realm);
  554. }
  555. octets = octets.Length == n
  556. ? octets
  557. : new byte[n];
  558. octets[0] = B;
  559. if (k + (3 * (n - 1)) >= strLen)
  560. {
  561. ExceptionHelper.ThrowUriError(_realm);
  562. }
  563. for (var j = 1; j < n; j++)
  564. {
  565. k++;
  566. if (uriString[k] != '%')
  567. {
  568. ExceptionHelper.ThrowUriError(_realm);
  569. }
  570. if (!IsValidHexaChar(uriString[k + 1]) || !IsValidHexaChar(uriString[k + 2]))
  571. {
  572. ExceptionHelper.ThrowUriError(_realm);
  573. }
  574. B = Convert.ToByte(uriString[k + 1].ToString() + uriString[k + 2], 16);
  575. // B & 11000000 != 10000000
  576. if ((B & 0xC0) != 0x80)
  577. {
  578. ExceptionHelper.ThrowUriError(_realm);
  579. }
  580. k += 2;
  581. octets[j] = B;
  582. }
  583. _stringBuilder.Append(Encoding.UTF8.GetString(octets, 0, octets.Length));
  584. }
  585. }
  586. }
  587. return _stringBuilder.ToString();
  588. }
  589. /// <summary>
  590. /// http://www.ecma-international.org/ecma-262/5.1/#sec-B.2.1
  591. /// </summary>
  592. public JsValue Escape(JsValue thisObject, JsValue[] arguments)
  593. {
  594. const string whiteList = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_ + -./";
  595. var uriString = TypeConverter.ToString(arguments.At(0));
  596. var strLen = uriString.Length;
  597. _stringBuilder.EnsureCapacity(strLen);
  598. _stringBuilder.Clear();
  599. for (var k = 0; k < strLen; k++)
  600. {
  601. var c = uriString[k];
  602. if (whiteList.IndexOf(c) != -1)
  603. {
  604. _stringBuilder.Append(c);
  605. }
  606. else if (c < 256)
  607. {
  608. _stringBuilder.Append($"%{((int) c):X2}");
  609. }
  610. else
  611. {
  612. _stringBuilder.Append($"%u{((int) c):X4}");
  613. }
  614. }
  615. return _stringBuilder.ToString();
  616. }
  617. /// <summary>
  618. /// http://www.ecma-international.org/ecma-262/5.1/#sec-B.2.2
  619. /// </summary>
  620. public JsValue Unescape(JsValue thisObject, JsValue[] arguments)
  621. {
  622. var uriString = TypeConverter.ToString(arguments.At(0));
  623. var strLen = uriString.Length;
  624. _stringBuilder.EnsureCapacity(strLen);
  625. _stringBuilder.Clear();
  626. for (var k = 0; k < strLen; k++)
  627. {
  628. var c = uriString[k];
  629. if (c == '%')
  630. {
  631. if (k <= strLen - 6
  632. && uriString[k + 1] == 'u'
  633. && uriString.Skip(k + 2).Take(4).All(IsValidHexaChar))
  634. {
  635. c = (char)int.Parse(
  636. string.Join(string.Empty, uriString.Skip(k + 2).Take(4)),
  637. NumberStyles.AllowHexSpecifier);
  638. k += 5;
  639. }
  640. else if (k <= strLen - 3
  641. && uriString.Skip(k + 1).Take(2).All(IsValidHexaChar))
  642. {
  643. c = (char)int.Parse(
  644. string.Join(string.Empty, uriString.Skip(k + 1).Take(2)),
  645. NumberStyles.AllowHexSpecifier);
  646. k += 2;
  647. }
  648. }
  649. _stringBuilder.Append(c);
  650. }
  651. return _stringBuilder.ToString();
  652. }
  653. // optimized versions with string parameter and without virtual dispatch for global environment usage
  654. internal bool HasProperty(Key property)
  655. {
  656. return GetOwnProperty(property) != PropertyDescriptor.Undefined;
  657. }
  658. internal PropertyDescriptor GetProperty(Key property) => GetOwnProperty(property);
  659. internal bool DefinePropertyOrThrow(Key property, PropertyDescriptor desc)
  660. {
  661. if (!DefineOwnProperty(property, desc))
  662. {
  663. ExceptionHelper.ThrowTypeError(_realm);
  664. }
  665. return true;
  666. }
  667. internal bool DefineOwnProperty(Key property, PropertyDescriptor desc)
  668. {
  669. var current = GetOwnProperty(property);
  670. if (current == desc)
  671. {
  672. return true;
  673. }
  674. // check fast path
  675. if ((current._flags & PropertyFlag.MutableBinding) != 0)
  676. {
  677. current._value = desc.Value;
  678. return true;
  679. }
  680. return ValidateAndApplyPropertyDescriptor(this, new JsString(property), true, desc, current);
  681. }
  682. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  683. internal PropertyDescriptor GetOwnProperty(Key property)
  684. {
  685. Properties.TryGetValue(property, out var descriptor);
  686. return descriptor ?? PropertyDescriptor.Undefined;
  687. }
  688. internal bool Set(Key property, JsValue value)
  689. {
  690. // here we are called only from global environment record context
  691. // we can take some shortcuts to be faster
  692. if (!_properties.TryGetValue(property, out var existingDescriptor))
  693. {
  694. _properties[property] = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
  695. return true;
  696. }
  697. if (existingDescriptor.IsDataDescriptor())
  698. {
  699. if (!existingDescriptor.Writable || existingDescriptor.IsAccessorDescriptor())
  700. {
  701. return false;
  702. }
  703. // check fast path
  704. if ((existingDescriptor._flags & PropertyFlag.MutableBinding) != 0)
  705. {
  706. existingDescriptor._value = value;
  707. return true;
  708. }
  709. // slow path
  710. return DefineOwnProperty(property, new PropertyDescriptor(value, PropertyFlag.None));
  711. }
  712. if (!(existingDescriptor.Set is ICallable setter))
  713. {
  714. return false;
  715. }
  716. setter.Call(this, new[] {value});
  717. return true;
  718. }
  719. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  720. internal void SetOwnProperty(Key property, PropertyDescriptor desc)
  721. {
  722. SetProperty(property, desc);
  723. }
  724. }
  725. }