236 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			236 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | 'use strict'; | ||
|  | 
 | ||
|  | var GetIntrinsic = require('./GetIntrinsic'); | ||
|  | 
 | ||
|  | var $Object = GetIntrinsic('%Object%'); | ||
|  | var $TypeError = GetIntrinsic('%TypeError%'); | ||
|  | var $String = GetIntrinsic('%String%'); | ||
|  | 
 | ||
|  | var assertRecord = require('./helpers/assertRecord'); | ||
|  | 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'); | ||
|  | 
 | ||
|  | // 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) { | ||
|  | 		if (this.Type(Desc) !== 'Object') { | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 		var allowed = { | ||
|  | 			'[[Configurable]]': true, | ||
|  | 			'[[Enumerable]]': true, | ||
|  | 			'[[Get]]': true, | ||
|  | 			'[[Set]]': true, | ||
|  | 			'[[Value]]': true, | ||
|  | 			'[[Writable]]': true | ||
|  | 		}; | ||
|  | 
 | ||
|  | 		for (var key in Desc) { // eslint-disable-line
 | ||
|  | 			if (has(Desc, key) && !allowed[key]) { | ||
|  | 				return false; | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		var isData = has(Desc, '[[Value]]'); | ||
|  | 		var IsAccessor = has(Desc, '[[Get]]') || has(Desc, '[[Set]]'); | ||
|  | 		if (isData && IsAccessor) { | ||
|  | 			throw new $TypeError('Property Descriptors may not be both accessor and data descriptors'); | ||
|  | 		} | ||
|  | 		return true; | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	// 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; | ||
|  | 	} | ||
|  | }; | ||
|  | 
 | ||
|  | module.exports = ES5; |