1'use strict'; 2 3var GetIntrinsic = require('../'); 4 5var test = require('tape'); 6var forEach = require('for-each'); 7var debug = require('object-inspect'); 8var generatorFns = require('make-generator-function')(); 9var asyncFns = require('make-async-function').list(); 10var asyncGenFns = require('make-async-generator-function')(); 11var mockProperty = require('mock-property'); 12 13var callBound = require('call-bind/callBound'); 14var v = require('es-value-fixtures'); 15var $gOPD = require('gopd'); 16var DefinePropertyOrThrow = require('es-abstract/2021/DefinePropertyOrThrow'); 17 18var $isProto = callBound('%Object.prototype.isPrototypeOf%'); 19 20test('export', function (t) { 21 t.equal(typeof GetIntrinsic, 'function', 'it is a function'); 22 t.equal(GetIntrinsic.length, 2, 'function has length of 2'); 23 24 t.end(); 25}); 26 27test('throws', function (t) { 28 t['throws']( 29 function () { GetIntrinsic('not an intrinsic'); }, 30 SyntaxError, 31 'nonexistent intrinsic throws a syntax error' 32 ); 33 34 t['throws']( 35 function () { GetIntrinsic(''); }, 36 TypeError, 37 'empty string intrinsic throws a type error' 38 ); 39 40 t['throws']( 41 function () { GetIntrinsic('.'); }, 42 SyntaxError, 43 '"just a dot" intrinsic throws a syntax error' 44 ); 45 46 t['throws']( 47 function () { GetIntrinsic('%String'); }, 48 SyntaxError, 49 'Leading % without trailing % throws a syntax error' 50 ); 51 52 t['throws']( 53 function () { GetIntrinsic('String%'); }, 54 SyntaxError, 55 'Trailing % without leading % throws a syntax error' 56 ); 57 58 t['throws']( 59 function () { GetIntrinsic("String['prototype]"); }, 60 SyntaxError, 61 'Dynamic property access is disallowed for intrinsics (unterminated string)' 62 ); 63 64 t['throws']( 65 function () { GetIntrinsic('%Proxy.prototype.undefined%'); }, 66 TypeError, 67 "Throws when middle part doesn't exist (%Proxy.prototype.undefined%)" 68 ); 69 70 t['throws']( 71 function () { GetIntrinsic('%Array.prototype%garbage%'); }, 72 SyntaxError, 73 'Throws with extra percent signs' 74 ); 75 76 t['throws']( 77 function () { GetIntrinsic('%Array.prototype%push%'); }, 78 SyntaxError, 79 'Throws with extra percent signs, even on an existing intrinsic' 80 ); 81 82 forEach(v.nonStrings, function (nonString) { 83 t['throws']( 84 function () { GetIntrinsic(nonString); }, 85 TypeError, 86 debug(nonString) + ' is not a String' 87 ); 88 }); 89 90 forEach(v.nonBooleans, function (nonBoolean) { 91 t['throws']( 92 function () { GetIntrinsic('%', nonBoolean); }, 93 TypeError, 94 debug(nonBoolean) + ' is not a Boolean' 95 ); 96 }); 97 98 forEach([ 99 'toString', 100 'propertyIsEnumerable', 101 'hasOwnProperty' 102 ], function (objectProtoMember) { 103 t['throws']( 104 function () { GetIntrinsic(objectProtoMember); }, 105 SyntaxError, 106 debug(objectProtoMember) + ' is not an intrinsic' 107 ); 108 }); 109 110 t.end(); 111}); 112 113test('base intrinsics', function (t) { 114 t.equal(GetIntrinsic('%Object%'), Object, '%Object% yields Object'); 115 t.equal(GetIntrinsic('Object'), Object, 'Object yields Object'); 116 t.equal(GetIntrinsic('%Array%'), Array, '%Array% yields Array'); 117 t.equal(GetIntrinsic('Array'), Array, 'Array yields Array'); 118 119 t.end(); 120}); 121 122test('dotted paths', function (t) { 123 t.equal(GetIntrinsic('%Object.prototype.toString%'), Object.prototype.toString, '%Object.prototype.toString% yields Object.prototype.toString'); 124 t.equal(GetIntrinsic('Object.prototype.toString'), Object.prototype.toString, 'Object.prototype.toString yields Object.prototype.toString'); 125 t.equal(GetIntrinsic('%Array.prototype.push%'), Array.prototype.push, '%Array.prototype.push% yields Array.prototype.push'); 126 t.equal(GetIntrinsic('Array.prototype.push'), Array.prototype.push, 'Array.prototype.push yields Array.prototype.push'); 127 128 test('underscore paths are aliases for dotted paths', { skip: !Object.isFrozen || Object.isFrozen(Object.prototype) }, function (st) { 129 var original = GetIntrinsic('%ObjProto_toString%'); 130 131 forEach([ 132 '%Object.prototype.toString%', 133 'Object.prototype.toString', 134 '%ObjectPrototype.toString%', 135 'ObjectPrototype.toString', 136 '%ObjProto_toString%', 137 'ObjProto_toString' 138 ], function (name) { 139 DefinePropertyOrThrow(Object.prototype, 'toString', { 140 '[[Value]]': function toString() { 141 return original.apply(this, arguments); 142 } 143 }); 144 st.equal(GetIntrinsic(name), original, name + ' yields original Object.prototype.toString'); 145 }); 146 147 DefinePropertyOrThrow(Object.prototype, 'toString', { '[[Value]]': original }); 148 st.end(); 149 }); 150 151 test('dotted paths cache', { skip: !Object.isFrozen || Object.isFrozen(Object.prototype) }, function (st) { 152 var original = GetIntrinsic('%Object.prototype.propertyIsEnumerable%'); 153 154 forEach([ 155 '%Object.prototype.propertyIsEnumerable%', 156 'Object.prototype.propertyIsEnumerable', 157 '%ObjectPrototype.propertyIsEnumerable%', 158 'ObjectPrototype.propertyIsEnumerable' 159 ], function (name) { 160 var restore = mockProperty(Object.prototype, 'propertyIsEnumerable', { 161 value: function propertyIsEnumerable() { 162 return original.apply(this, arguments); 163 } 164 }); 165 st.equal(GetIntrinsic(name), original, name + ' yields cached Object.prototype.propertyIsEnumerable'); 166 167 restore(); 168 }); 169 170 st.end(); 171 }); 172 173 test('dotted path reports correct error', function (st) { 174 st['throws'](function () { 175 GetIntrinsic('%NonExistentIntrinsic.prototype.property%'); 176 }, /%NonExistentIntrinsic%/, 'The base intrinsic of %NonExistentIntrinsic.prototype.property% is %NonExistentIntrinsic%'); 177 178 st['throws'](function () { 179 GetIntrinsic('%NonExistentIntrinsicPrototype.property%'); 180 }, /%NonExistentIntrinsicPrototype%/, 'The base intrinsic of %NonExistentIntrinsicPrototype.property% is %NonExistentIntrinsicPrototype%'); 181 182 st.end(); 183 }); 184 185 t.end(); 186}); 187 188test('accessors', { skip: !$gOPD || typeof Map !== 'function' }, function (t) { 189 var actual = $gOPD(Map.prototype, 'size'); 190 t.ok(actual, 'Map.prototype.size has a descriptor'); 191 t.equal(typeof actual.get, 'function', 'Map.prototype.size has a getter function'); 192 t.equal(GetIntrinsic('%Map.prototype.size%'), actual.get, '%Map.prototype.size% yields the getter for it'); 193 t.equal(GetIntrinsic('Map.prototype.size'), actual.get, 'Map.prototype.size yields the getter for it'); 194 195 t.end(); 196}); 197 198test('generator functions', { skip: !generatorFns.length }, function (t) { 199 var $GeneratorFunction = GetIntrinsic('%GeneratorFunction%'); 200 var $GeneratorFunctionPrototype = GetIntrinsic('%Generator%'); 201 var $GeneratorPrototype = GetIntrinsic('%GeneratorPrototype%'); 202 203 forEach(generatorFns, function (genFn) { 204 var fnName = genFn.name; 205 fnName = fnName ? "'" + fnName + "'" : 'genFn'; 206 207 t.ok(genFn instanceof $GeneratorFunction, fnName + ' instanceof %GeneratorFunction%'); 208 t.ok($isProto($GeneratorFunctionPrototype, genFn), '%Generator% is prototype of ' + fnName); 209 t.ok($isProto($GeneratorPrototype, genFn.prototype), '%GeneratorPrototype% is prototype of ' + fnName + '.prototype'); 210 }); 211 212 t.end(); 213}); 214 215test('async functions', { skip: !asyncFns.length }, function (t) { 216 var $AsyncFunction = GetIntrinsic('%AsyncFunction%'); 217 var $AsyncFunctionPrototype = GetIntrinsic('%AsyncFunctionPrototype%'); 218 219 forEach(asyncFns, function (asyncFn) { 220 var fnName = asyncFn.name; 221 fnName = fnName ? "'" + fnName + "'" : 'asyncFn'; 222 223 t.ok(asyncFn instanceof $AsyncFunction, fnName + ' instanceof %AsyncFunction%'); 224 t.ok($isProto($AsyncFunctionPrototype, asyncFn), '%AsyncFunctionPrototype% is prototype of ' + fnName); 225 }); 226 227 t.end(); 228}); 229 230test('async generator functions', { skip: asyncGenFns.length === 0 }, function (t) { 231 var $AsyncGeneratorFunction = GetIntrinsic('%AsyncGeneratorFunction%'); 232 var $AsyncGeneratorFunctionPrototype = GetIntrinsic('%AsyncGenerator%'); 233 var $AsyncGeneratorPrototype = GetIntrinsic('%AsyncGeneratorPrototype%'); 234 235 forEach(asyncGenFns, function (asyncGenFn) { 236 var fnName = asyncGenFn.name; 237 fnName = fnName ? "'" + fnName + "'" : 'asyncGenFn'; 238 239 t.ok(asyncGenFn instanceof $AsyncGeneratorFunction, fnName + ' instanceof %AsyncGeneratorFunction%'); 240 t.ok($isProto($AsyncGeneratorFunctionPrototype, asyncGenFn), '%AsyncGenerator% is prototype of ' + fnName); 241 t.ok($isProto($AsyncGeneratorPrototype, asyncGenFn.prototype), '%AsyncGeneratorPrototype% is prototype of ' + fnName + '.prototype'); 242 }); 243 244 t.end(); 245}); 246 247test('%ThrowTypeError%', function (t) { 248 var $ThrowTypeError = GetIntrinsic('%ThrowTypeError%'); 249 250 t.equal(typeof $ThrowTypeError, 'function', 'is a function'); 251 t['throws']( 252 $ThrowTypeError, 253 TypeError, 254 '%ThrowTypeError% throws a TypeError' 255 ); 256 257 t.end(); 258}); 259 260test('allowMissing', { skip: asyncGenFns.length > 0 }, function (t) { 261 t['throws']( 262 function () { GetIntrinsic('%AsyncGeneratorPrototype%'); }, 263 TypeError, 264 'throws when missing' 265 ); 266 267 t.equal( 268 GetIntrinsic('%AsyncGeneratorPrototype%', true), 269 undefined, 270 'does not throw when allowMissing' 271 ); 272 273 t.end(); 274}); 275