You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

544 lines
15 KiB

4 years ago
  1. 'use strict';
  2. var GetIntrinsic = require('./GetIntrinsic');
  3. var $Object = GetIntrinsic('%Object%');
  4. var $EvalError = GetIntrinsic('%EvalError%');
  5. var $TypeError = GetIntrinsic('%TypeError%');
  6. var $String = GetIntrinsic('%String%');
  7. var $Date = GetIntrinsic('%Date%');
  8. var $Number = GetIntrinsic('%Number%');
  9. var $floor = GetIntrinsic('%Math.floor%');
  10. var $DateUTC = GetIntrinsic('%Date.UTC%');
  11. var $abs = GetIntrinsic('%Math.abs%');
  12. var assertRecord = require('./helpers/assertRecord');
  13. var isPropertyDescriptor = require('./helpers/isPropertyDescriptor');
  14. var $isNaN = require('./helpers/isNaN');
  15. var $isFinite = require('./helpers/isFinite');
  16. var sign = require('./helpers/sign');
  17. var mod = require('./helpers/mod');
  18. var isPrefixOf = require('./helpers/isPrefixOf');
  19. var callBound = require('./helpers/callBound');
  20. var IsCallable = require('is-callable');
  21. var toPrimitive = require('es-to-primitive/es5');
  22. var has = require('has');
  23. var $getUTCFullYear = callBound('Date.prototype.getUTCFullYear');
  24. var HoursPerDay = 24;
  25. var MinutesPerHour = 60;
  26. var SecondsPerMinute = 60;
  27. var msPerSecond = 1e3;
  28. var msPerMinute = msPerSecond * SecondsPerMinute;
  29. var msPerHour = msPerMinute * MinutesPerHour;
  30. var msPerDay = 86400000;
  31. // https://es5.github.io/#x9
  32. var ES5 = {
  33. ToPrimitive: toPrimitive,
  34. ToBoolean: function ToBoolean(value) {
  35. return !!value;
  36. },
  37. ToNumber: function ToNumber(value) {
  38. return +value; // eslint-disable-line no-implicit-coercion
  39. },
  40. ToInteger: function ToInteger(value) {
  41. var number = this.ToNumber(value);
  42. if ($isNaN(number)) { return 0; }
  43. if (number === 0 || !$isFinite(number)) { return number; }
  44. return sign(number) * Math.floor(Math.abs(number));
  45. },
  46. ToInt32: function ToInt32(x) {
  47. return this.ToNumber(x) >> 0;
  48. },
  49. ToUint32: function ToUint32(x) {
  50. return this.ToNumber(x) >>> 0;
  51. },
  52. ToUint16: function ToUint16(value) {
  53. var number = this.ToNumber(value);
  54. if ($isNaN(number) || number === 0 || !$isFinite(number)) { return 0; }
  55. var posInt = sign(number) * Math.floor(Math.abs(number));
  56. return mod(posInt, 0x10000);
  57. },
  58. ToString: function ToString(value) {
  59. return $String(value);
  60. },
  61. ToObject: function ToObject(value) {
  62. this.CheckObjectCoercible(value);
  63. return $Object(value);
  64. },
  65. CheckObjectCoercible: function CheckObjectCoercible(value, optMessage) {
  66. /* jshint eqnull:true */
  67. if (value == null) {
  68. throw new $TypeError(optMessage || 'Cannot call method on ' + value);
  69. }
  70. return value;
  71. },
  72. IsCallable: IsCallable,
  73. SameValue: function SameValue(x, y) {
  74. if (x === y) { // 0 === -0, but they are not identical.
  75. if (x === 0) { return 1 / x === 1 / y; }
  76. return true;
  77. }
  78. return $isNaN(x) && $isNaN(y);
  79. },
  80. // https://ecma-international.org/ecma-262/5.1/#sec-8
  81. Type: function Type(x) {
  82. if (x === null) {
  83. return 'Null';
  84. }
  85. if (typeof x === 'undefined') {
  86. return 'Undefined';
  87. }
  88. if (typeof x === 'function' || typeof x === 'object') {
  89. return 'Object';
  90. }
  91. if (typeof x === 'number') {
  92. return 'Number';
  93. }
  94. if (typeof x === 'boolean') {
  95. return 'Boolean';
  96. }
  97. if (typeof x === 'string') {
  98. return 'String';
  99. }
  100. },
  101. // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type
  102. IsPropertyDescriptor: function IsPropertyDescriptor(Desc) {
  103. return isPropertyDescriptor(this, Desc);
  104. },
  105. // https://ecma-international.org/ecma-262/5.1/#sec-8.10.1
  106. IsAccessorDescriptor: function IsAccessorDescriptor(Desc) {
  107. if (typeof Desc === 'undefined') {
  108. return false;
  109. }
  110. assertRecord(this, 'Property Descriptor', 'Desc', Desc);
  111. if (!has(Desc, '[[Get]]') && !has(Desc, '[[Set]]')) {
  112. return false;
  113. }
  114. return true;
  115. },
  116. // https://ecma-international.org/ecma-262/5.1/#sec-8.10.2
  117. IsDataDescriptor: function IsDataDescriptor(Desc) {
  118. if (typeof Desc === 'undefined') {
  119. return false;
  120. }
  121. assertRecord(this, 'Property Descriptor', 'Desc', Desc);
  122. if (!has(Desc, '[[Value]]') && !has(Desc, '[[Writable]]')) {
  123. return false;
  124. }
  125. return true;
  126. },
  127. // https://ecma-international.org/ecma-262/5.1/#sec-8.10.3
  128. IsGenericDescriptor: function IsGenericDescriptor(Desc) {
  129. if (typeof Desc === 'undefined') {
  130. return false;
  131. }
  132. assertRecord(this, 'Property Descriptor', 'Desc', Desc);
  133. if (!this.IsAccessorDescriptor(Desc) && !this.IsDataDescriptor(Desc)) {
  134. return true;
  135. }
  136. return false;
  137. },
  138. // https://ecma-international.org/ecma-262/5.1/#sec-8.10.4
  139. FromPropertyDescriptor: function FromPropertyDescriptor(Desc) {
  140. if (typeof Desc === 'undefined') {
  141. return Desc;
  142. }
  143. assertRecord(this, 'Property Descriptor', 'Desc', Desc);
  144. if (this.IsDataDescriptor(Desc)) {
  145. return {
  146. value: Desc['[[Value]]'],
  147. writable: !!Desc['[[Writable]]'],
  148. enumerable: !!Desc['[[Enumerable]]'],
  149. configurable: !!Desc['[[Configurable]]']
  150. };
  151. } else if (this.IsAccessorDescriptor(Desc)) {
  152. return {
  153. get: Desc['[[Get]]'],
  154. set: Desc['[[Set]]'],
  155. enumerable: !!Desc['[[Enumerable]]'],
  156. configurable: !!Desc['[[Configurable]]']
  157. };
  158. } else {
  159. throw new $TypeError('FromPropertyDescriptor must be called with a fully populated Property Descriptor');
  160. }
  161. },
  162. // https://ecma-international.org/ecma-262/5.1/#sec-8.10.5
  163. ToPropertyDescriptor: function ToPropertyDescriptor(Obj) {
  164. if (this.Type(Obj) !== 'Object') {
  165. throw new $TypeError('ToPropertyDescriptor requires an object');
  166. }
  167. var desc = {};
  168. if (has(Obj, 'enumerable')) {
  169. desc['[[Enumerable]]'] = this.ToBoolean(Obj.enumerable);
  170. }
  171. if (has(Obj, 'configurable')) {
  172. desc['[[Configurable]]'] = this.ToBoolean(Obj.configurable);
  173. }
  174. if (has(Obj, 'value')) {
  175. desc['[[Value]]'] = Obj.value;
  176. }
  177. if (has(Obj, 'writable')) {
  178. desc['[[Writable]]'] = this.ToBoolean(Obj.writable);
  179. }
  180. if (has(Obj, 'get')) {
  181. var getter = Obj.get;
  182. if (typeof getter !== 'undefined' && !this.IsCallable(getter)) {
  183. throw new TypeError('getter must be a function');
  184. }
  185. desc['[[Get]]'] = getter;
  186. }
  187. if (has(Obj, 'set')) {
  188. var setter = Obj.set;
  189. if (typeof setter !== 'undefined' && !this.IsCallable(setter)) {
  190. throw new $TypeError('setter must be a function');
  191. }
  192. desc['[[Set]]'] = setter;
  193. }
  194. if ((has(desc, '[[Get]]') || has(desc, '[[Set]]')) && (has(desc, '[[Value]]') || has(desc, '[[Writable]]'))) {
  195. throw new $TypeError('Invalid property descriptor. Cannot both specify accessors and a value or writable attribute');
  196. }
  197. return desc;
  198. },
  199. // https://ecma-international.org/ecma-262/5.1/#sec-11.9.3
  200. 'Abstract Equality Comparison': function AbstractEqualityComparison(x, y) {
  201. var xType = this.Type(x);
  202. var yType = this.Type(y);
  203. if (xType === yType) {
  204. return x === y; // ES6+ specified this shortcut anyways.
  205. }
  206. if (x == null && y == null) {
  207. return true;
  208. }
  209. if (xType === 'Number' && yType === 'String') {
  210. return this['Abstract Equality Comparison'](x, this.ToNumber(y));
  211. }
  212. if (xType === 'String' && yType === 'Number') {
  213. return this['Abstract Equality Comparison'](this.ToNumber(x), y);
  214. }
  215. if (xType === 'Boolean') {
  216. return this['Abstract Equality Comparison'](this.ToNumber(x), y);
  217. }
  218. if (yType === 'Boolean') {
  219. return this['Abstract Equality Comparison'](x, this.ToNumber(y));
  220. }
  221. if ((xType === 'String' || xType === 'Number') && yType === 'Object') {
  222. return this['Abstract Equality Comparison'](x, this.ToPrimitive(y));
  223. }
  224. if (xType === 'Object' && (yType === 'String' || yType === 'Number')) {
  225. return this['Abstract Equality Comparison'](this.ToPrimitive(x), y);
  226. }
  227. return false;
  228. },
  229. // https://ecma-international.org/ecma-262/5.1/#sec-11.9.6
  230. 'Strict Equality Comparison': function StrictEqualityComparison(x, y) {
  231. var xType = this.Type(x);
  232. var yType = this.Type(y);
  233. if (xType !== yType) {
  234. return false;
  235. }
  236. if (xType === 'Undefined' || xType === 'Null') {
  237. return true;
  238. }
  239. return x === y; // shortcut for steps 4-7
  240. },
  241. // https://ecma-international.org/ecma-262/5.1/#sec-11.8.5
  242. // eslint-disable-next-line max-statements
  243. 'Abstract Relational Comparison': function AbstractRelationalComparison(x, y, LeftFirst) {
  244. if (this.Type(LeftFirst) !== 'Boolean') {
  245. throw new $TypeError('Assertion failed: LeftFirst argument must be a Boolean');
  246. }
  247. var px;
  248. var py;
  249. if (LeftFirst) {
  250. px = this.ToPrimitive(x, $Number);
  251. py = this.ToPrimitive(y, $Number);
  252. } else {
  253. py = this.ToPrimitive(y, $Number);
  254. px = this.ToPrimitive(x, $Number);
  255. }
  256. var bothStrings = this.Type(px) === 'String' && this.Type(py) === 'String';
  257. if (!bothStrings) {
  258. var nx = this.ToNumber(px);
  259. var ny = this.ToNumber(py);
  260. if ($isNaN(nx) || $isNaN(ny)) {
  261. return undefined;
  262. }
  263. if ($isFinite(nx) && $isFinite(ny) && nx === ny) {
  264. return false;
  265. }
  266. if (nx === 0 && ny === 0) {
  267. return false;
  268. }
  269. if (nx === Infinity) {
  270. return false;
  271. }
  272. if (ny === Infinity) {
  273. return true;
  274. }
  275. if (ny === -Infinity) {
  276. return false;
  277. }
  278. if (nx === -Infinity) {
  279. return true;
  280. }
  281. return nx < ny; // by now, these are both nonzero, finite, and not equal
  282. }
  283. if (isPrefixOf(py, px)) {
  284. return false;
  285. }
  286. if (isPrefixOf(px, py)) {
  287. return true;
  288. }
  289. return px < py; // both strings, neither a prefix of the other. shortcut for steps c-f
  290. },
  291. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.10
  292. msFromTime: function msFromTime(t) {
  293. return mod(t, msPerSecond);
  294. },
  295. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.10
  296. SecFromTime: function SecFromTime(t) {
  297. return mod($floor(t / msPerSecond), SecondsPerMinute);
  298. },
  299. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.10
  300. MinFromTime: function MinFromTime(t) {
  301. return mod($floor(t / msPerMinute), MinutesPerHour);
  302. },
  303. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.10
  304. HourFromTime: function HourFromTime(t) {
  305. return mod($floor(t / msPerHour), HoursPerDay);
  306. },
  307. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.2
  308. Day: function Day(t) {
  309. return $floor(t / msPerDay);
  310. },
  311. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.2
  312. TimeWithinDay: function TimeWithinDay(t) {
  313. return mod(t, msPerDay);
  314. },
  315. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.3
  316. DayFromYear: function DayFromYear(y) {
  317. return (365 * (y - 1970)) + $floor((y - 1969) / 4) - $floor((y - 1901) / 100) + $floor((y - 1601) / 400);
  318. },
  319. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.3
  320. TimeFromYear: function TimeFromYear(y) {
  321. return msPerDay * this.DayFromYear(y);
  322. },
  323. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.3
  324. YearFromTime: function YearFromTime(t) {
  325. // largest y such that this.TimeFromYear(y) <= t
  326. return $getUTCFullYear(new $Date(t));
  327. },
  328. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.6
  329. WeekDay: function WeekDay(t) {
  330. return mod(this.Day(t) + 4, 7);
  331. },
  332. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.3
  333. DaysInYear: function DaysInYear(y) {
  334. if (mod(y, 4) !== 0) {
  335. return 365;
  336. }
  337. if (mod(y, 100) !== 0) {
  338. return 366;
  339. }
  340. if (mod(y, 400) !== 0) {
  341. return 365;
  342. }
  343. return 366;
  344. },
  345. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.3
  346. InLeapYear: function InLeapYear(t) {
  347. var days = this.DaysInYear(this.YearFromTime(t));
  348. if (days === 365) {
  349. return 0;
  350. }
  351. if (days === 366) {
  352. return 1;
  353. }
  354. throw new $EvalError('Assertion failed: there are not 365 or 366 days in a year, got: ' + days);
  355. },
  356. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.4
  357. DayWithinYear: function DayWithinYear(t) {
  358. return this.Day(t) - this.DayFromYear(this.YearFromTime(t));
  359. },
  360. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.4
  361. MonthFromTime: function MonthFromTime(t) {
  362. var day = this.DayWithinYear(t);
  363. if (0 <= day && day < 31) {
  364. return 0;
  365. }
  366. var leap = this.InLeapYear(t);
  367. if (31 <= day && day < (59 + leap)) {
  368. return 1;
  369. }
  370. if ((59 + leap) <= day && day < (90 + leap)) {
  371. return 2;
  372. }
  373. if ((90 + leap) <= day && day < (120 + leap)) {
  374. return 3;
  375. }
  376. if ((120 + leap) <= day && day < (151 + leap)) {
  377. return 4;
  378. }
  379. if ((151 + leap) <= day && day < (181 + leap)) {
  380. return 5;
  381. }
  382. if ((181 + leap) <= day && day < (212 + leap)) {
  383. return 6;
  384. }
  385. if ((212 + leap) <= day && day < (243 + leap)) {
  386. return 7;
  387. }
  388. if ((243 + leap) <= day && day < (273 + leap)) {
  389. return 8;
  390. }
  391. if ((273 + leap) <= day && day < (304 + leap)) {
  392. return 9;
  393. }
  394. if ((304 + leap) <= day && day < (334 + leap)) {
  395. return 10;
  396. }
  397. if ((334 + leap) <= day && day < (365 + leap)) {
  398. return 11;
  399. }
  400. },
  401. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.5
  402. DateFromTime: function DateFromTime(t) {
  403. var m = this.MonthFromTime(t);
  404. var d = this.DayWithinYear(t);
  405. if (m === 0) {
  406. return d + 1;
  407. }
  408. if (m === 1) {
  409. return d - 30;
  410. }
  411. var leap = this.InLeapYear(t);
  412. if (m === 2) {
  413. return d - 58 - leap;
  414. }
  415. if (m === 3) {
  416. return d - 89 - leap;
  417. }
  418. if (m === 4) {
  419. return d - 119 - leap;
  420. }
  421. if (m === 5) {
  422. return d - 150 - leap;
  423. }
  424. if (m === 6) {
  425. return d - 180 - leap;
  426. }
  427. if (m === 7) {
  428. return d - 211 - leap;
  429. }
  430. if (m === 8) {
  431. return d - 242 - leap;
  432. }
  433. if (m === 9) {
  434. return d - 272 - leap;
  435. }
  436. if (m === 10) {
  437. return d - 303 - leap;
  438. }
  439. if (m === 11) {
  440. return d - 333 - leap;
  441. }
  442. throw new $EvalError('Assertion failed: MonthFromTime returned an impossible value: ' + m);
  443. },
  444. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.12
  445. MakeDay: function MakeDay(year, month, date) {
  446. if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) {
  447. return NaN;
  448. }
  449. var y = this.ToInteger(year);
  450. var m = this.ToInteger(month);
  451. var dt = this.ToInteger(date);
  452. var ym = y + $floor(m / 12);
  453. var mn = mod(m, 12);
  454. var t = $DateUTC(ym, mn, 1);
  455. if (this.YearFromTime(t) !== ym || this.MonthFromTime(t) !== mn || this.DateFromTime(t) !== 1) {
  456. return NaN;
  457. }
  458. return this.Day(t) + dt - 1;
  459. },
  460. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.13
  461. MakeDate: function MakeDate(day, time) {
  462. if (!$isFinite(day) || !$isFinite(time)) {
  463. return NaN;
  464. }
  465. return (day * msPerDay) + time;
  466. },
  467. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.11
  468. MakeTime: function MakeTime(hour, min, sec, ms) {
  469. if (!$isFinite(hour) || !$isFinite(min) || !$isFinite(sec) || !$isFinite(ms)) {
  470. return NaN;
  471. }
  472. var h = this.ToInteger(hour);
  473. var m = this.ToInteger(min);
  474. var s = this.ToInteger(sec);
  475. var milli = this.ToInteger(ms);
  476. var t = (h * msPerHour) + (m * msPerMinute) + (s * msPerSecond) + milli;
  477. return t;
  478. },
  479. // https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.14
  480. TimeClip: function TimeClip(time) {
  481. if (!$isFinite(time) || $abs(time) > 8.64e15) {
  482. return NaN;
  483. }
  484. return $Number(new $Date(this.ToNumber(time)));
  485. },
  486. // https://ecma-international.org/ecma-262/5.1/#sec-5.2
  487. modulo: function modulo(x, y) {
  488. return mod(x, y);
  489. }
  490. };
  491. module.exports = ES5;