'use strict'; var GetIntrinsic = require('./GetIntrinsic'); var $Object = GetIntrinsic('%Object%'); var $TypeError = GetIntrinsic('%TypeError%'); var $String = GetIntrinsic('%String%'); var $Number = GetIntrinsic('%Number%'); var assertRecord = require('./helpers/assertRecord'); var isPropertyDescriptor = require('./helpers/isPropertyDescriptor'); var $isNaN = require('./helpers/isNaN'); var $isFinite = require('./helpers/isFinite'); var sign = require('./helpers/sign'); var mod = require('./helpers/mod'); var IsCallable = require('is-callable'); var toPrimitive = require('es-to-primitive/es5'); var has = require('has'); var callBind = require('./helpers/callBind'); var strSlice = callBind($String.prototype.slice); var isPrefixOf = function isPrefixOf(prefix, string) { if (prefix === string) { return true; } if (prefix.length > string.length) { return false; } return strSlice(string, 0, prefix.length) === prefix; }; // https://es5.github.io/#x9 var ES5 = { ToPrimitive: toPrimitive, ToBoolean: function ToBoolean(value) { return !!value; }, ToNumber: function ToNumber(value) { return +value; // eslint-disable-line no-implicit-coercion }, ToInteger: function ToInteger(value) { var number = this.ToNumber(value); if ($isNaN(number)) { return 0; } if (number === 0 || !$isFinite(number)) { return number; } return sign(number) * Math.floor(Math.abs(number)); }, ToInt32: function ToInt32(x) { return this.ToNumber(x) >> 0; }, ToUint32: function ToUint32(x) { return this.ToNumber(x) >>> 0; }, ToUint16: function ToUint16(value) { var number = this.ToNumber(value); if ($isNaN(number) || number === 0 || !$isFinite(number)) { return 0; } var posInt = sign(number) * Math.floor(Math.abs(number)); return mod(posInt, 0x10000); }, ToString: function ToString(value) { return $String(value); }, ToObject: function ToObject(value) { this.CheckObjectCoercible(value); return $Object(value); }, CheckObjectCoercible: function CheckObjectCoercible(value, optMessage) { /* jshint eqnull:true */ if (value == null) { throw new $TypeError(optMessage || 'Cannot call method on ' + value); } return value; }, IsCallable: IsCallable, SameValue: function SameValue(x, y) { if (x === y) { // 0 === -0, but they are not identical. if (x === 0) { return 1 / x === 1 / y; } return true; } return $isNaN(x) && $isNaN(y); }, // https://www.ecma-international.org/ecma-262/5.1/#sec-8 Type: function Type(x) { if (x === null) { return 'Null'; } if (typeof x === 'undefined') { return 'Undefined'; } if (typeof x === 'function' || typeof x === 'object') { return 'Object'; } if (typeof x === 'number') { return 'Number'; } if (typeof x === 'boolean') { return 'Boolean'; } if (typeof x === 'string') { return 'String'; } }, // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type IsPropertyDescriptor: function IsPropertyDescriptor(Desc) { return isPropertyDescriptor(this, Desc); }, // https://ecma-international.org/ecma-262/5.1/#sec-8.10.1 IsAccessorDescriptor: function IsAccessorDescriptor(Desc) { if (typeof Desc === 'undefined') { return false; } assertRecord(this, 'Property Descriptor', 'Desc', Desc); if (!has(Desc, '[[Get]]') && !has(Desc, '[[Set]]')) { return false; } return true; }, // https://ecma-international.org/ecma-262/5.1/#sec-8.10.2 IsDataDescriptor: function IsDataDescriptor(Desc) { if (typeof Desc === 'undefined') { return false; } assertRecord(this, 'Property Descriptor', 'Desc', Desc); if (!has(Desc, '[[Value]]') && !has(Desc, '[[Writable]]')) { return false; } return true; }, // https://ecma-international.org/ecma-262/5.1/#sec-8.10.3 IsGenericDescriptor: function IsGenericDescriptor(Desc) { if (typeof Desc === 'undefined') { return false; } assertRecord(this, 'Property Descriptor', 'Desc', Desc); if (!this.IsAccessorDescriptor(Desc) && !this.IsDataDescriptor(Desc)) { return true; } return false; }, // https://ecma-international.org/ecma-262/5.1/#sec-8.10.4 FromPropertyDescriptor: function FromPropertyDescriptor(Desc) { if (typeof Desc === 'undefined') { return Desc; } assertRecord(this, 'Property Descriptor', 'Desc', Desc); if (this.IsDataDescriptor(Desc)) { return { value: Desc['[[Value]]'], writable: !!Desc['[[Writable]]'], enumerable: !!Desc['[[Enumerable]]'], configurable: !!Desc['[[Configurable]]'] }; } else if (this.IsAccessorDescriptor(Desc)) { return { get: Desc['[[Get]]'], set: Desc['[[Set]]'], enumerable: !!Desc['[[Enumerable]]'], configurable: !!Desc['[[Configurable]]'] }; } else { throw new $TypeError('FromPropertyDescriptor must be called with a fully populated Property Descriptor'); } }, // https://ecma-international.org/ecma-262/5.1/#sec-8.10.5 ToPropertyDescriptor: function ToPropertyDescriptor(Obj) { if (this.Type(Obj) !== 'Object') { throw new $TypeError('ToPropertyDescriptor requires an object'); } var desc = {}; if (has(Obj, 'enumerable')) { desc['[[Enumerable]]'] = this.ToBoolean(Obj.enumerable); } if (has(Obj, 'configurable')) { desc['[[Configurable]]'] = this.ToBoolean(Obj.configurable); } if (has(Obj, 'value')) { desc['[[Value]]'] = Obj.value; } if (has(Obj, 'writable')) { desc['[[Writable]]'] = this.ToBoolean(Obj.writable); } if (has(Obj, 'get')) { var getter = Obj.get; if (typeof getter !== 'undefined' && !this.IsCallable(getter)) { throw new TypeError('getter must be a function'); } desc['[[Get]]'] = getter; } if (has(Obj, 'set')) { var setter = Obj.set; if (typeof setter !== 'undefined' && !this.IsCallable(setter)) { throw new $TypeError('setter must be a function'); } desc['[[Set]]'] = setter; } if ((has(desc, '[[Get]]') || has(desc, '[[Set]]')) && (has(desc, '[[Value]]') || has(desc, '[[Writable]]'))) { throw new $TypeError('Invalid property descriptor. Cannot both specify accessors and a value or writable attribute'); } return desc; }, // https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3 'Abstract Equality Comparison': function AbstractEqualityComparison(x, y) { var xType = this.Type(x); var yType = this.Type(y); if (xType === yType) { return x === y; // ES6+ specified this shortcut anyways. } if (x == null && y == null) { return true; } if (xType === 'Number' && yType === 'String') { return this['Abstract Equality Comparison'](x, this.ToNumber(y)); } if (xType === 'String' && yType === 'Number') { return this['Abstract Equality Comparison'](this.ToNumber(x), y); } if (xType === 'Boolean') { return this['Abstract Equality Comparison'](this.ToNumber(x), y); } if (yType === 'Boolean') { return this['Abstract Equality Comparison'](x, this.ToNumber(y)); } if ((xType === 'String' || xType === 'Number') && yType === 'Object') { return this['Abstract Equality Comparison'](x, this.ToPrimitive(y)); } if (xType === 'Object' && (yType === 'String' || yType === 'Number')) { return this['Abstract Equality Comparison'](this.ToPrimitive(x), y); } return false; }, // https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.6 'Strict Equality Comparison': function StrictEqualityComparison(x, y) { var xType = this.Type(x); var yType = this.Type(y); if (xType !== yType) { return false; } if (xType === 'Undefined' || xType === 'Null') { return true; } return x === y; // shortcut for steps 4-7 }, // https://www.ecma-international.org/ecma-262/5.1/#sec-11.8.5 // eslint-disable-next-line max-statements 'Abstract Relational Comparison': function AbstractRelationalComparison(x, y, LeftFirst) { if (this.Type(LeftFirst) !== 'Boolean') { throw new $TypeError('Assertion failed: LeftFirst argument must be a Boolean'); } var px; var py; if (LeftFirst) { px = this.ToPrimitive(x, $Number); py = this.ToPrimitive(y, $Number); } else { py = this.ToPrimitive(y, $Number); px = this.ToPrimitive(x, $Number); } var bothStrings = this.Type(px) === 'String' && this.Type(py) === 'String'; if (!bothStrings) { var nx = this.ToNumber(px); var ny = this.ToNumber(py); if ($isNaN(nx) || $isNaN(ny)) { return undefined; } if ($isFinite(nx) && $isFinite(ny) && nx === ny) { return false; } if (nx === 0 && ny === 0) { return false; } if (nx === Infinity) { return false; } if (ny === Infinity) { return true; } if (ny === -Infinity) { return false; } if (nx === -Infinity) { return true; } return nx < ny; // by now, these are both nonzero, finite, and not equal } if (isPrefixOf(py, px)) { return false; } if (isPrefixOf(px, py)) { return true; } return px < py; // both strings, neither a prefix of the other. shortcut for steps c-f } }; module.exports = ES5;