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.

289 lines
9.3 KiB

4 years ago
  1. 'use strict';
  2. var GetIntrinsic = require('./GetIntrinsic');
  3. var keys = require('object-keys');
  4. var inspect = require('object-inspect');
  5. var ES2017 = require('./es2017');
  6. var assign = require('./helpers/assign');
  7. var forEach = require('./helpers/forEach');
  8. var callBind = require('./helpers/callBind');
  9. var every = require('./helpers/every');
  10. var isPrefixOf = require('./helpers/isPrefixOf');
  11. var $String = GetIntrinsic('%String%');
  12. var $TypeError = GetIntrinsic('%TypeError%');
  13. var callBound = require('./helpers/callBound');
  14. var regexTester = require('./helpers/regexTester');
  15. var $isNaN = require('./helpers/isNaN');
  16. var $SymbolValueOf = callBound('Symbol.prototype.valueOf', true);
  17. // var $charAt = callBound('String.prototype.charAt');
  18. var $strSlice = callBound('String.prototype.slice');
  19. var $indexOf = callBound('String.prototype.indexOf');
  20. var $parseInt = parseInt;
  21. var isDigit = regexTester(/^[0-9]$/);
  22. var $PromiseResolve = callBound('Promise.resolve', true);
  23. var $isEnumerable = callBound('Object.prototype.propertyIsEnumerable');
  24. var $pushApply = callBind.apply(GetIntrinsic('%Array.prototype.push%'));
  25. var $gOPS = $SymbolValueOf ? GetIntrinsic('%Object.getOwnPropertySymbols%') : null;
  26. var padTimeComponent = function padTimeComponent(c, count) {
  27. return $strSlice('00' + c, -(count || 2));
  28. };
  29. var weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
  30. var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  31. var OwnPropertyKeys = function OwnPropertyKeys(ES, source) {
  32. var ownKeys = keys(source);
  33. if ($gOPS) {
  34. $pushApply(ownKeys, $gOPS(source));
  35. }
  36. return ownKeys;
  37. };
  38. var ES2018 = assign(assign({}, ES2017), {
  39. EnumerableOwnPropertyNames: ES2017.EnumerableOwnProperties,
  40. // https://ecma-international.org/ecma-262/9.0/#sec-thissymbolvalue
  41. thisSymbolValue: function thisSymbolValue(value) {
  42. if (!$SymbolValueOf) {
  43. throw new SyntaxError('Symbols are not supported; thisSymbolValue requires that `value` be a Symbol or a Symbol object');
  44. }
  45. if (this.Type(value) === 'Symbol') {
  46. return value;
  47. }
  48. return $SymbolValueOf(value);
  49. },
  50. // https://www.ecma-international.org/ecma-262/9.0/#sec-isstringprefix
  51. IsStringPrefix: function IsStringPrefix(p, q) {
  52. if (this.Type(p) !== 'String') {
  53. throw new TypeError('Assertion failed: "p" must be a String');
  54. }
  55. if (this.Type(q) !== 'String') {
  56. throw new TypeError('Assertion failed: "q" must be a String');
  57. }
  58. return isPrefixOf(p, q);
  59. /*
  60. if (p === q || p === '') {
  61. return true;
  62. }
  63. var pLength = p.length;
  64. var qLength = q.length;
  65. if (pLength >= qLength) {
  66. return false;
  67. }
  68. // assert: pLength < qLength
  69. for (var i = 0; i < pLength; i += 1) {
  70. if ($charAt(p, i) !== $charAt(q, i)) {
  71. return false;
  72. }
  73. }
  74. return true;
  75. */
  76. },
  77. // https://www.ecma-international.org/ecma-262/9.0/#sec-tostring-applied-to-the-number-type
  78. NumberToString: function NumberToString(m) {
  79. if (this.Type(m) !== 'Number') {
  80. throw new TypeError('Assertion failed: "m" must be a String');
  81. }
  82. return $String(m);
  83. },
  84. // https://www.ecma-international.org/ecma-262/9.0/#sec-copydataproperties
  85. CopyDataProperties: function CopyDataProperties(target, source, excludedItems) {
  86. if (this.Type(target) !== 'Object') {
  87. throw new TypeError('Assertion failed: "target" must be an Object');
  88. }
  89. if (!this.IsArray(excludedItems)) {
  90. throw new TypeError('Assertion failed: "excludedItems" must be a List of Property Keys');
  91. }
  92. for (var i = 0; i < excludedItems.length; i += 1) {
  93. if (!this.IsPropertyKey(excludedItems[i])) {
  94. throw new TypeError('Assertion failed: "excludedItems" must be a List of Property Keys');
  95. }
  96. }
  97. if (typeof source === 'undefined' || source === null) {
  98. return target;
  99. }
  100. var ES = this;
  101. var fromObj = ES.ToObject(source);
  102. var sourceKeys = OwnPropertyKeys(ES, fromObj);
  103. forEach(sourceKeys, function (nextKey) {
  104. var excluded = false;
  105. forEach(excludedItems, function (e) {
  106. if (ES.SameValue(e, nextKey) === true) {
  107. excluded = true;
  108. }
  109. });
  110. var enumerable = $isEnumerable(fromObj, nextKey) || (
  111. // this is to handle string keys being non-enumerable in older engines
  112. typeof source === 'string'
  113. && nextKey >= 0
  114. && ES.IsInteger(ES.ToNumber(nextKey))
  115. );
  116. if (excluded === false && enumerable) {
  117. var propValue = ES.Get(fromObj, nextKey);
  118. ES.CreateDataProperty(target, nextKey, propValue);
  119. }
  120. });
  121. return target;
  122. },
  123. // https://ecma-international.org/ecma-262/9.0/#sec-promise-resolve
  124. PromiseResolve: function PromiseResolve(C, x) {
  125. if (!$PromiseResolve) {
  126. throw new SyntaxError('This environment does not support Promises.');
  127. }
  128. return $PromiseResolve(C, x);
  129. },
  130. // http://www.ecma-international.org/ecma-262/9.0/#sec-getsubstitution
  131. // eslint-disable-next-line max-statements, max-params, max-lines-per-function
  132. GetSubstitution: function GetSubstitution(matched, str, position, captures, namedCaptures, replacement) {
  133. if (this.Type(matched) !== 'String') {
  134. throw new $TypeError('Assertion failed: `matched` must be a String');
  135. }
  136. var matchLength = matched.length;
  137. if (this.Type(str) !== 'String') {
  138. throw new $TypeError('Assertion failed: `str` must be a String');
  139. }
  140. var stringLength = str.length;
  141. if (!this.IsInteger(position) || position < 0 || position > stringLength) {
  142. throw new $TypeError('Assertion failed: `position` must be a nonnegative integer, and less than or equal to the length of `string`, got ' + inspect(position));
  143. }
  144. var ES = this;
  145. var isStringOrHole = function (capture, index, arr) { return ES.Type(capture) === 'String' || !(index in arr); };
  146. if (!this.IsArray(captures) || !every(captures, isStringOrHole)) {
  147. throw new $TypeError('Assertion failed: `captures` must be a List of Strings, got ' + inspect(captures));
  148. }
  149. if (this.Type(replacement) !== 'String') {
  150. throw new $TypeError('Assertion failed: `replacement` must be a String');
  151. }
  152. var tailPos = position + matchLength;
  153. var m = captures.length;
  154. if (this.Type(namedCaptures) !== 'Undefined') {
  155. namedCaptures = this.ToObject(namedCaptures); // eslint-disable-line no-param-reassign
  156. }
  157. var result = '';
  158. for (var i = 0; i < replacement.length; i += 1) {
  159. // if this is a $, and it's not the end of the replacement
  160. var current = replacement[i];
  161. var isLast = (i + 1) >= replacement.length;
  162. var nextIsLast = (i + 2) >= replacement.length;
  163. if (current === '$' && !isLast) {
  164. var next = replacement[i + 1];
  165. if (next === '$') {
  166. result += '$';
  167. i += 1;
  168. } else if (next === '&') {
  169. result += matched;
  170. i += 1;
  171. } else if (next === '`') {
  172. result += position === 0 ? '' : $strSlice(str, 0, position - 1);
  173. i += 1;
  174. } else if (next === "'") {
  175. result += tailPos >= stringLength ? '' : $strSlice(str, tailPos);
  176. i += 1;
  177. } else {
  178. var nextNext = nextIsLast ? null : replacement[i + 2];
  179. if (isDigit(next) && next !== '0' && (nextIsLast || !isDigit(nextNext))) {
  180. // $1 through $9, and not followed by a digit
  181. var n = $parseInt(next, 10);
  182. // if (n > m, impl-defined)
  183. result += (n <= m && this.Type(captures[n - 1]) === 'Undefined') ? '' : captures[n - 1];
  184. i += 1;
  185. } else if (isDigit(next) && (nextIsLast || isDigit(nextNext))) {
  186. // $00 through $99
  187. var nn = next + nextNext;
  188. var nnI = $parseInt(nn, 10) - 1;
  189. // if nn === '00' or nn > m, impl-defined
  190. result += (nn <= m && this.Type(captures[nnI]) === 'Undefined') ? '' : captures[nnI];
  191. i += 2;
  192. } else if (next === '<') {
  193. // eslint-disable-next-line max-depth
  194. if (this.Type(namedCaptures) === 'Undefined') {
  195. result += '$<';
  196. i += 2;
  197. } else {
  198. var endIndex = $indexOf(replacement, '>', i);
  199. // eslint-disable-next-line max-depth
  200. if (endIndex > -1) {
  201. var groupName = $strSlice(replacement, i, endIndex);
  202. var capture = this.Get(namedCaptures, groupName);
  203. // eslint-disable-next-line max-depth
  204. if (this.Type(capture) !== 'Undefined') {
  205. result += this.ToString(capture);
  206. }
  207. i += '$<' + groupName + '>'.length;
  208. }
  209. }
  210. } else {
  211. result += '$';
  212. }
  213. }
  214. } else {
  215. // the final $, or else not a $
  216. result += replacement[i];
  217. }
  218. }
  219. return result;
  220. },
  221. // https://www.ecma-international.org/ecma-262/9.0/#sec-datestring
  222. DateString: function DateString(tv) {
  223. if (this.Type(tv) !== 'Number' || $isNaN(tv)) {
  224. throw new $TypeError('Assertion failed: `tv` must be a non-NaN Number');
  225. }
  226. var weekday = weekdays[this.WeekDay(tv)];
  227. var month = months[this.MonthFromTime(tv)];
  228. var day = padTimeComponent(this.DateFromTime(tv));
  229. var year = padTimeComponent(this.YearFromTime(tv), 4);
  230. return weekday + '\x20' + month + '\x20' + day + '\x20' + year;
  231. },
  232. // https://www.ecma-international.org/ecma-262/9.0/#sec-timestring
  233. TimeString: function TimeString(tv) {
  234. if (this.Type(tv) !== 'Number' || $isNaN(tv)) {
  235. throw new $TypeError('Assertion failed: `tv` must be a non-NaN Number');
  236. }
  237. var hour = this.HourFromTime(tv);
  238. var minute = this.MinFromTime(tv);
  239. var second = this.SecFromTime(tv);
  240. return padTimeComponent(hour) + ':' + padTimeComponent(minute) + ':' + padTimeComponent(second) + '\x20GMT';
  241. }
  242. });
  243. delete ES2018.EnumerableOwnProperties; // replaced with EnumerableOwnPropertyNames
  244. delete ES2018.IsPropertyDescriptor; // not an actual abstract operation
  245. module.exports = ES2018;