1'use strict'; 2 3var GetIntrinsic = require('./GetIntrinsic'); 4 5var $Object = GetIntrinsic('%Object%'); 6var $TypeError = GetIntrinsic('%TypeError%'); 7var $String = GetIntrinsic('%String%'); 8 9var $isNaN = require('./helpers/isNaN'); 10var $isFinite = require('./helpers/isFinite'); 11 12var sign = require('./helpers/sign'); 13var mod = require('./helpers/mod'); 14 15var IsCallable = require('is-callable'); 16var toPrimitive = require('es-to-primitive/es5'); 17 18var has = require('has'); 19 20// https://es5.github.io/#x9 21var ES5 = { 22 ToPrimitive: toPrimitive, 23 24 ToBoolean: function ToBoolean(value) { 25 return !!value; 26 }, 27 ToNumber: function ToNumber(value) { 28 return +value; // eslint-disable-line no-implicit-coercion 29 }, 30 ToInteger: function ToInteger(value) { 31 var number = this.ToNumber(value); 32 if ($isNaN(number)) { return 0; } 33 if (number === 0 || !$isFinite(number)) { return number; } 34 return sign(number) * Math.floor(Math.abs(number)); 35 }, 36 ToInt32: function ToInt32(x) { 37 return this.ToNumber(x) >> 0; 38 }, 39 ToUint32: function ToUint32(x) { 40 return this.ToNumber(x) >>> 0; 41 }, 42 ToUint16: function ToUint16(value) { 43 var number = this.ToNumber(value); 44 if ($isNaN(number) || number === 0 || !$isFinite(number)) { return 0; } 45 var posInt = sign(number) * Math.floor(Math.abs(number)); 46 return mod(posInt, 0x10000); 47 }, 48 ToString: function ToString(value) { 49 return $String(value); 50 }, 51 ToObject: function ToObject(value) { 52 this.CheckObjectCoercible(value); 53 return $Object(value); 54 }, 55 CheckObjectCoercible: function CheckObjectCoercible(value, optMessage) { 56 /* jshint eqnull:true */ 57 if (value == null) { 58 throw new $TypeError(optMessage || 'Cannot call method on ' + value); 59 } 60 return value; 61 }, 62 IsCallable: IsCallable, 63 SameValue: function SameValue(x, y) { 64 if (x === y) { // 0 === -0, but they are not identical. 65 if (x === 0) { return 1 / x === 1 / y; } 66 return true; 67 } 68 return $isNaN(x) && $isNaN(y); 69 }, 70 71 // https://www.ecma-international.org/ecma-262/5.1/#sec-8 72 Type: function Type(x) { 73 if (x === null) { 74 return 'Null'; 75 } 76 if (typeof x === 'undefined') { 77 return 'Undefined'; 78 } 79 if (typeof x === 'function' || typeof x === 'object') { 80 return 'Object'; 81 } 82 if (typeof x === 'number') { 83 return 'Number'; 84 } 85 if (typeof x === 'boolean') { 86 return 'Boolean'; 87 } 88 if (typeof x === 'string') { 89 return 'String'; 90 } 91 }, 92 93 // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type 94 IsPropertyDescriptor: function IsPropertyDescriptor(Desc) { 95 if (this.Type(Desc) !== 'Object') { 96 return false; 97 } 98 var allowed = { 99 '[[Configurable]]': true, 100 '[[Enumerable]]': true, 101 '[[Get]]': true, 102 '[[Set]]': true, 103 '[[Value]]': true, 104 '[[Writable]]': true 105 }; 106 // jscs:disable 107 for (var key in Desc) { // eslint-disable-line 108 if (has(Desc, key) && !allowed[key]) { 109 return false; 110 } 111 } 112 // jscs:enable 113 var isData = has(Desc, '[[Value]]'); 114 var IsAccessor = has(Desc, '[[Get]]') || has(Desc, '[[Set]]'); 115 if (isData && IsAccessor) { 116 throw new $TypeError('Property Descriptors may not be both accessor and data descriptors'); 117 } 118 return true; 119 }, 120 121 // https://ecma-international.org/ecma-262/5.1/#sec-8.10.1 122 IsAccessorDescriptor: function IsAccessorDescriptor(Desc) { 123 if (typeof Desc === 'undefined') { 124 return false; 125 } 126 127 if (!this.IsPropertyDescriptor(Desc)) { 128 throw new $TypeError('Desc must be a Property Descriptor'); 129 } 130 131 if (!has(Desc, '[[Get]]') && !has(Desc, '[[Set]]')) { 132 return false; 133 } 134 135 return true; 136 }, 137 138 // https://ecma-international.org/ecma-262/5.1/#sec-8.10.2 139 IsDataDescriptor: function IsDataDescriptor(Desc) { 140 if (typeof Desc === 'undefined') { 141 return false; 142 } 143 144 if (!this.IsPropertyDescriptor(Desc)) { 145 throw new $TypeError('Desc must be a Property Descriptor'); 146 } 147 148 if (!has(Desc, '[[Value]]') && !has(Desc, '[[Writable]]')) { 149 return false; 150 } 151 152 return true; 153 }, 154 155 // https://ecma-international.org/ecma-262/5.1/#sec-8.10.3 156 IsGenericDescriptor: function IsGenericDescriptor(Desc) { 157 if (typeof Desc === 'undefined') { 158 return false; 159 } 160 161 if (!this.IsPropertyDescriptor(Desc)) { 162 throw new $TypeError('Desc must be a Property Descriptor'); 163 } 164 165 if (!this.IsAccessorDescriptor(Desc) && !this.IsDataDescriptor(Desc)) { 166 return true; 167 } 168 169 return false; 170 }, 171 172 // https://ecma-international.org/ecma-262/5.1/#sec-8.10.4 173 FromPropertyDescriptor: function FromPropertyDescriptor(Desc) { 174 if (typeof Desc === 'undefined') { 175 return Desc; 176 } 177 178 if (!this.IsPropertyDescriptor(Desc)) { 179 throw new $TypeError('Desc must be a Property Descriptor'); 180 } 181 182 if (this.IsDataDescriptor(Desc)) { 183 return { 184 value: Desc['[[Value]]'], 185 writable: !!Desc['[[Writable]]'], 186 enumerable: !!Desc['[[Enumerable]]'], 187 configurable: !!Desc['[[Configurable]]'] 188 }; 189 } else if (this.IsAccessorDescriptor(Desc)) { 190 return { 191 get: Desc['[[Get]]'], 192 set: Desc['[[Set]]'], 193 enumerable: !!Desc['[[Enumerable]]'], 194 configurable: !!Desc['[[Configurable]]'] 195 }; 196 } else { 197 throw new $TypeError('FromPropertyDescriptor must be called with a fully populated Property Descriptor'); 198 } 199 }, 200 201 // https://ecma-international.org/ecma-262/5.1/#sec-8.10.5 202 ToPropertyDescriptor: function ToPropertyDescriptor(Obj) { 203 if (this.Type(Obj) !== 'Object') { 204 throw new $TypeError('ToPropertyDescriptor requires an object'); 205 } 206 207 var desc = {}; 208 if (has(Obj, 'enumerable')) { 209 desc['[[Enumerable]]'] = this.ToBoolean(Obj.enumerable); 210 } 211 if (has(Obj, 'configurable')) { 212 desc['[[Configurable]]'] = this.ToBoolean(Obj.configurable); 213 } 214 if (has(Obj, 'value')) { 215 desc['[[Value]]'] = Obj.value; 216 } 217 if (has(Obj, 'writable')) { 218 desc['[[Writable]]'] = this.ToBoolean(Obj.writable); 219 } 220 if (has(Obj, 'get')) { 221 var getter = Obj.get; 222 if (typeof getter !== 'undefined' && !this.IsCallable(getter)) { 223 throw new TypeError('getter must be a function'); 224 } 225 desc['[[Get]]'] = getter; 226 } 227 if (has(Obj, 'set')) { 228 var setter = Obj.set; 229 if (typeof setter !== 'undefined' && !this.IsCallable(setter)) { 230 throw new $TypeError('setter must be a function'); 231 } 232 desc['[[Set]]'] = setter; 233 } 234 235 if ((has(desc, '[[Get]]') || has(desc, '[[Set]]')) && (has(desc, '[[Value]]') || has(desc, '[[Writable]]'))) { 236 throw new $TypeError('Invalid property descriptor. Cannot both specify accessors and a value or writable attribute'); 237 } 238 return desc; 239 } 240}; 241 242module.exports = ES5; 243