• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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