es5.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. 'use strict';
  2. var GetIntrinsic = require('./GetIntrinsic');
  3. var $Object = GetIntrinsic('%Object%');
  4. var $TypeError = GetIntrinsic('%TypeError%');
  5. var $String = GetIntrinsic('%String%');
  6. var $Number = GetIntrinsic('%Number%');
  7. var assertRecord = require('./helpers/assertRecord');
  8. var isPropertyDescriptor = require('./helpers/isPropertyDescriptor');
  9. var $isNaN = require('./helpers/isNaN');
  10. var $isFinite = require('./helpers/isFinite');
  11. var sign = require('./helpers/sign');
  12. var mod = require('./helpers/mod');
  13. var IsCallable = require('is-callable');
  14. var toPrimitive = require('es-to-primitive/es5');
  15. var has = require('has');
  16. var callBind = require('./helpers/callBind');
  17. var strSlice = callBind($String.prototype.slice);
  18. var isPrefixOf = function isPrefixOf(prefix, string) {
  19. if (prefix === string) {
  20. return true;
  21. }
  22. if (prefix.length > string.length) {
  23. return false;
  24. }
  25. return strSlice(string, 0, prefix.length) === prefix;
  26. };
  27. // https://es5.github.io/#x9
  28. var ES5 = {
  29. ToPrimitive: toPrimitive,
  30. ToBoolean: function ToBoolean(value) {
  31. return !!value;
  32. },
  33. ToNumber: function ToNumber(value) {
  34. return +value; // eslint-disable-line no-implicit-coercion
  35. },
  36. ToInteger: function ToInteger(value) {
  37. var number = this.ToNumber(value);
  38. if ($isNaN(number)) { return 0; }
  39. if (number === 0 || !$isFinite(number)) { return number; }
  40. return sign(number) * Math.floor(Math.abs(number));
  41. },
  42. ToInt32: function ToInt32(x) {
  43. return this.ToNumber(x) >> 0;
  44. },
  45. ToUint32: function ToUint32(x) {
  46. return this.ToNumber(x) >>> 0;
  47. },
  48. ToUint16: function ToUint16(value) {
  49. var number = this.ToNumber(value);
  50. if ($isNaN(number) || number === 0 || !$isFinite(number)) { return 0; }
  51. var posInt = sign(number) * Math.floor(Math.abs(number));
  52. return mod(posInt, 0x10000);
  53. },
  54. ToString: function ToString(value) {
  55. return $String(value);
  56. },
  57. ToObject: function ToObject(value) {
  58. this.CheckObjectCoercible(value);
  59. return $Object(value);
  60. },
  61. CheckObjectCoercible: function CheckObjectCoercible(value, optMessage) {
  62. /* jshint eqnull:true */
  63. if (value == null) {
  64. throw new $TypeError(optMessage || 'Cannot call method on ' + value);
  65. }
  66. return value;
  67. },
  68. IsCallable: IsCallable,
  69. SameValue: function SameValue(x, y) {
  70. if (x === y) { // 0 === -0, but they are not identical.
  71. if (x === 0) { return 1 / x === 1 / y; }
  72. return true;
  73. }
  74. return $isNaN(x) && $isNaN(y);
  75. },
  76. // https://www.ecma-international.org/ecma-262/5.1/#sec-8
  77. Type: function Type(x) {
  78. if (x === null) {
  79. return 'Null';
  80. }
  81. if (typeof x === 'undefined') {
  82. return 'Undefined';
  83. }
  84. if (typeof x === 'function' || typeof x === 'object') {
  85. return 'Object';
  86. }
  87. if (typeof x === 'number') {
  88. return 'Number';
  89. }
  90. if (typeof x === 'boolean') {
  91. return 'Boolean';
  92. }
  93. if (typeof x === 'string') {
  94. return 'String';
  95. }
  96. },
  97. // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type
  98. IsPropertyDescriptor: function IsPropertyDescriptor(Desc) {
  99. return isPropertyDescriptor(this, Desc);
  100. },
  101. // https://ecma-international.org/ecma-262/5.1/#sec-8.10.1
  102. IsAccessorDescriptor: function IsAccessorDescriptor(Desc) {
  103. if (typeof Desc === 'undefined') {
  104. return false;
  105. }
  106. assertRecord(this, 'Property Descriptor', 'Desc', Desc);
  107. if (!has(Desc, '[[Get]]') && !has(Desc, '[[Set]]')) {
  108. return false;
  109. }
  110. return true;
  111. },
  112. // https://ecma-international.org/ecma-262/5.1/#sec-8.10.2
  113. IsDataDescriptor: function IsDataDescriptor(Desc) {
  114. if (typeof Desc === 'undefined') {
  115. return false;
  116. }
  117. assertRecord(this, 'Property Descriptor', 'Desc', Desc);
  118. if (!has(Desc, '[[Value]]') && !has(Desc, '[[Writable]]')) {
  119. return false;
  120. }
  121. return true;
  122. },
  123. // https://ecma-international.org/ecma-262/5.1/#sec-8.10.3
  124. IsGenericDescriptor: function IsGenericDescriptor(Desc) {
  125. if (typeof Desc === 'undefined') {
  126. return false;
  127. }
  128. assertRecord(this, 'Property Descriptor', 'Desc', Desc);
  129. if (!this.IsAccessorDescriptor(Desc) && !this.IsDataDescriptor(Desc)) {
  130. return true;
  131. }
  132. return false;
  133. },
  134. // https://ecma-international.org/ecma-262/5.1/#sec-8.10.4
  135. FromPropertyDescriptor: function FromPropertyDescriptor(Desc) {
  136. if (typeof Desc === 'undefined') {
  137. return Desc;
  138. }
  139. assertRecord(this, 'Property Descriptor', 'Desc', Desc);
  140. if (this.IsDataDescriptor(Desc)) {
  141. return {
  142. value: Desc['[[Value]]'],
  143. writable: !!Desc['[[Writable]]'],
  144. enumerable: !!Desc['[[Enumerable]]'],
  145. configurable: !!Desc['[[Configurable]]']
  146. };
  147. } else if (this.IsAccessorDescriptor(Desc)) {
  148. return {
  149. get: Desc['[[Get]]'],
  150. set: Desc['[[Set]]'],
  151. enumerable: !!Desc['[[Enumerable]]'],
  152. configurable: !!Desc['[[Configurable]]']
  153. };
  154. } else {
  155. throw new $TypeError('FromPropertyDescriptor must be called with a fully populated Property Descriptor');
  156. }
  157. },
  158. // https://ecma-international.org/ecma-262/5.1/#sec-8.10.5
  159. ToPropertyDescriptor: function ToPropertyDescriptor(Obj) {
  160. if (this.Type(Obj) !== 'Object') {
  161. throw new $TypeError('ToPropertyDescriptor requires an object');
  162. }
  163. var desc = {};
  164. if (has(Obj, 'enumerable')) {
  165. desc['[[Enumerable]]'] = this.ToBoolean(Obj.enumerable);
  166. }
  167. if (has(Obj, 'configurable')) {
  168. desc['[[Configurable]]'] = this.ToBoolean(Obj.configurable);
  169. }
  170. if (has(Obj, 'value')) {
  171. desc['[[Value]]'] = Obj.value;
  172. }
  173. if (has(Obj, 'writable')) {
  174. desc['[[Writable]]'] = this.ToBoolean(Obj.writable);
  175. }
  176. if (has(Obj, 'get')) {
  177. var getter = Obj.get;
  178. if (typeof getter !== 'undefined' && !this.IsCallable(getter)) {
  179. throw new TypeError('getter must be a function');
  180. }
  181. desc['[[Get]]'] = getter;
  182. }
  183. if (has(Obj, 'set')) {
  184. var setter = Obj.set;
  185. if (typeof setter !== 'undefined' && !this.IsCallable(setter)) {
  186. throw new $TypeError('setter must be a function');
  187. }
  188. desc['[[Set]]'] = setter;
  189. }
  190. if ((has(desc, '[[Get]]') || has(desc, '[[Set]]')) && (has(desc, '[[Value]]') || has(desc, '[[Writable]]'))) {
  191. throw new $TypeError('Invalid property descriptor. Cannot both specify accessors and a value or writable attribute');
  192. }
  193. return desc;
  194. },
  195. // https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3
  196. 'Abstract Equality Comparison': function AbstractEqualityComparison(x, y) {
  197. var xType = this.Type(x);
  198. var yType = this.Type(y);
  199. if (xType === yType) {
  200. return x === y; // ES6+ specified this shortcut anyways.
  201. }
  202. if (x == null && y == null) {
  203. return true;
  204. }
  205. if (xType === 'Number' && yType === 'String') {
  206. return this['Abstract Equality Comparison'](x, this.ToNumber(y));
  207. }
  208. if (xType === 'String' && yType === 'Number') {
  209. return this['Abstract Equality Comparison'](this.ToNumber(x), y);
  210. }
  211. if (xType === 'Boolean') {
  212. return this['Abstract Equality Comparison'](this.ToNumber(x), y);
  213. }
  214. if (yType === 'Boolean') {
  215. return this['Abstract Equality Comparison'](x, this.ToNumber(y));
  216. }
  217. if ((xType === 'String' || xType === 'Number') && yType === 'Object') {
  218. return this['Abstract Equality Comparison'](x, this.ToPrimitive(y));
  219. }
  220. if (xType === 'Object' && (yType === 'String' || yType === 'Number')) {
  221. return this['Abstract Equality Comparison'](this.ToPrimitive(x), y);
  222. }
  223. return false;
  224. },
  225. // https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.6
  226. 'Strict Equality Comparison': function StrictEqualityComparison(x, y) {
  227. var xType = this.Type(x);
  228. var yType = this.Type(y);
  229. if (xType !== yType) {
  230. return false;
  231. }
  232. if (xType === 'Undefined' || xType === 'Null') {
  233. return true;
  234. }
  235. return x === y; // shortcut for steps 4-7
  236. },
  237. // https://www.ecma-international.org/ecma-262/5.1/#sec-11.8.5
  238. // eslint-disable-next-line max-statements
  239. 'Abstract Relational Comparison': function AbstractRelationalComparison(x, y, LeftFirst) {
  240. if (this.Type(LeftFirst) !== 'Boolean') {
  241. throw new $TypeError('Assertion failed: LeftFirst argument must be a Boolean');
  242. }
  243. var px;
  244. var py;
  245. if (LeftFirst) {
  246. px = this.ToPrimitive(x, $Number);
  247. py = this.ToPrimitive(y, $Number);
  248. } else {
  249. py = this.ToPrimitive(y, $Number);
  250. px = this.ToPrimitive(x, $Number);
  251. }
  252. var bothStrings = this.Type(px) === 'String' && this.Type(py) === 'String';
  253. if (!bothStrings) {
  254. var nx = this.ToNumber(px);
  255. var ny = this.ToNumber(py);
  256. if ($isNaN(nx) || $isNaN(ny)) {
  257. return undefined;
  258. }
  259. if ($isFinite(nx) && $isFinite(ny) && nx === ny) {
  260. return false;
  261. }
  262. if (nx === 0 && ny === 0) {
  263. return false;
  264. }
  265. if (nx === Infinity) {
  266. return false;
  267. }
  268. if (ny === Infinity) {
  269. return true;
  270. }
  271. if (ny === -Infinity) {
  272. return false;
  273. }
  274. if (nx === -Infinity) {
  275. return true;
  276. }
  277. return nx < ny; // by now, these are both nonzero, finite, and not equal
  278. }
  279. if (isPrefixOf(py, px)) {
  280. return false;
  281. }
  282. if (isPrefixOf(px, py)) {
  283. return true;
  284. }
  285. return px < py; // both strings, neither a prefix of the other. shortcut for steps c-f
  286. }
  287. };
  288. module.exports = ES5;