├── test.js ├── polyfill.js └── README.md /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Builtin = require('./polyfill'); 4 | const assert = require('assert'); 5 | 6 | // Verify normal builtins 7 | assert.strictEqual(Builtin.typeOf(Date), 'function'); 8 | assert.strictEqual(Builtin.typeOf(new Date()), 'Date'); 9 | assert.strictEqual(Builtin.typeOf(Reflect), 'object'); 10 | 11 | class M { 12 | static [Symbol.builtin]() { 13 | return 'M'; 14 | } 15 | }; 16 | 17 | class N {} 18 | 19 | // Verify that a class with @@builtin is reported as a builtin 20 | assert.strictEqual(Builtin.typeOf(new M()), 'M'); 21 | assert.strictEqual(Builtin.typeOf(M), 'function'); 22 | 23 | // Verify that a class without @@builtin is not reported as a builtin 24 | assert.strictEqual(Builtin.typeOf(new N()), 'object'); 25 | assert.strictEqual(Builtin.typeOf(N), 'function'); 26 | 27 | // Verify that we can mask a built-in 28 | Date[Symbol.builtin] = undefined; 29 | assert.strictEqual(Builtin.typeOf(new Date()), 'object'); 30 | -------------------------------------------------------------------------------- /polyfill.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Note: this polyfill is currently limited from working completely across 4 | // platforms 5 | 6 | const kSymbol = Symbol('Symbol.builtin'); 7 | const kBuiltin = Symbol('[[Builtin]]'); 8 | const { isAsyncFunction } = process.binding('util'); 9 | 10 | Object.defineProperty(Symbol, 'builtin', { 11 | enumerable: false, 12 | configurable: false, 13 | value: kSymbol 14 | }); 15 | 16 | function GetBuiltinValue(value) { 17 | if (value === null) 18 | return; 19 | const fn = value[kSymbol]; 20 | if (fn === undefined) return; 21 | const val = fn.call(value); 22 | if (val === undefined) return; 23 | return `${val}`; 24 | } 25 | 26 | function GetOwnBuiltinValue(value) { 27 | if (value === null) 28 | return; 29 | if (!Object.getOwnPropertyDescriptor(value, kSymbol)) 30 | return; 31 | return GetBuiltinValue(value); 32 | } 33 | 34 | function builtin() { 35 | if (typeof this !== 'object' && typeof this !== 'function') 36 | throw new TypeError('Method invoked on a value that is not an object'); 37 | return this[kBuiltin]; 38 | } 39 | 40 | const AsyncFunction = (async () => {}).constructor; 41 | const GeneratorFunction = (function*() {}).constructor; 42 | 43 | const labels = [ 44 | Array, 'Array', 45 | ArrayBuffer, 'ArrayBuffer', 46 | AsyncFunction, 'AsyncFunction', 47 | Boolean, 'Boolean', 48 | DataView, 'DataView', 49 | Date, 'Date', 50 | Error, 'Error', 51 | EvalError, 'EvalError', 52 | Float32Array, 'Float32Array', 53 | Float64Array, 'Float64Array', 54 | Function, 'function', 55 | GeneratorFunction, 'GeneratorFunction', 56 | Int8Array, 'Int8Array', 57 | Int16Array, 'Int16Array', 58 | Int32Array, 'Int32Array', 59 | JSON, 'JSON', 60 | Map, 'Map', 61 | Math, 'Math', 62 | Number, 'Number', 63 | Object, 'object', 64 | Promise, 'Promise', 65 | Proxy, 'Proxy', 66 | RangeError, 'RangeError', 67 | ReferenceError, 'ReferenceError', 68 | Reflect, 'Reflect', 69 | RegExp, 'RegExp', 70 | Set, 'Set', 71 | String, 'String', 72 | Symbol, 'Symbol', 73 | SyntaxError, 'SyntaxError', 74 | TypeError, 'TypeError', 75 | Uint8Array, 'Uint8Array', 76 | Uint8ClampedArray, 'Uint8ClampedArray', 77 | Uint16Array, 'Uint16Array', 78 | Uint32Array, 'Uint32Array', 79 | URIError, 'URIError', 80 | WeakMap, 'WeakMap', 81 | WeakSet, 'WeakSet' 82 | ]; 83 | for (let n = 0; n < labels.length; n = n + 2) { 84 | Object.defineProperties(labels[n], { 85 | [kBuiltin]: { 86 | enumerable: false, 87 | configurable: false, 88 | value: labels[n + 1] 89 | }, 90 | [kSymbol]: { 91 | enumerable: false, 92 | configurable: true, 93 | writable: true, 94 | value: builtin 95 | }}); 96 | } 97 | 98 | if (typeof Atomics !== 'undefined') { 99 | Object.defineProperties(Atomics, { 100 | [kBuiltin]: { 101 | enumerable: false, 102 | configurable: false, 103 | value: 'Atomics' 104 | }, 105 | [kSymbol]: { 106 | enumerable: false, 107 | configurable: true, 108 | writable: true, 109 | value: builtin 110 | }}); 111 | } 112 | 113 | if (typeof SharedArrayBuffer !== 'undefined') { 114 | Object.defineProperties(SharedArrayBuffer, { 115 | [kBuiltin]: { 116 | enumerable: false, 117 | configurable: false, 118 | value: 'SharedArrayBuffer' 119 | }, 120 | [kSymbol]: { 121 | enumerable: false, 122 | configurable: true, 123 | writable: true, 124 | value: builtin 125 | }}); 126 | } 127 | 128 | function _is(value1, value2) { 129 | if (typeof value1 !== 'object' && typeof value1 !== 'function') 130 | return false; 131 | const v1 = GetOwnBuiltinValue(value1); 132 | if (v1 === undefined) 133 | return false; 134 | if (value2 === undefined) 135 | return false; 136 | if (typeof value2 !== 'object' && typeof value1 !== 'function') 137 | return false; 138 | const v2 = GetOwnBuiltinValue(value2); 139 | return v1 === v2; 140 | } 141 | 142 | function _typeof(value) { 143 | if (typeof value === 'object' || typeof value === 'function') { 144 | const ctor = value.constructor; 145 | if (ctor !== undefined) { 146 | const val = GetBuiltinValue(ctor); 147 | if (val !== undefined) 148 | return val; 149 | } 150 | } 151 | return typeof value; 152 | } 153 | 154 | Object.defineProperty(builtin, 'name', { value: '@@builtin' }); 155 | Object.defineProperty(_is, 'name', { value: 'is' }); 156 | Object.defineProperty(_typeof, 'name', { value: 'typeof' }); 157 | 158 | const Builtin = {}; 159 | Object.defineProperties(Builtin, { 160 | 'is': { 161 | enumerable: false, 162 | configurable: true, 163 | writable: true, 164 | value: _is 165 | }, 166 | 'typeOf': { 167 | enumerable: false, 168 | configurable: true, 169 | writable: true, 170 | value: _typeof 171 | } 172 | }); 173 | 174 | module.exports = Builtin; 175 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Builtin.is and Builtin.typeOf 2 | 3 | ## Motivation 4 | 5 | There are a number of situations where existing type checking using `instanceof` 6 | can be problematic. For instance: 7 | 8 | ```js 9 | $ ./node 10 | > (new Date()) instanceof Date 11 | true 12 | > (vm.runInNewContext('new Date()')) instanceof Date 13 | false 14 | ``` 15 | 16 | In this case, both statements return valid `Date` objects. However, because 17 | the second is created in a separate realm, it is not recognized as a `Date` in 18 | the current realm, despite operating appropriately in every other respect. 19 | 20 | In other cases, `instanceof` does not provide adequate granularity, such as 21 | checking if a given argument is an unsigned 16-bit integer vs. a signed 32-bit 22 | integer. 23 | 24 | This proposal introduces a new `Builtin` built-in object that exposes methods 25 | that allow reliable cross-realm type checking for ECMAScript built-ins. 26 | 27 | ## Prior art 28 | 29 | Node.js has relied on such checks, in part, to reliably determine types for 30 | debugging, inspection and display formatting purposes in the `util.format()` 31 | and `util.inspect()` APIs. In addition, the `is` package on npm (which 32 | implements similar type checks) currently has roughly 33k+ downloads per day. 33 | 34 | Node.js can (and has) implement these functions in a host-specific manner as 35 | part of the Node.js API but the preference would be towards having these kind 36 | of type checks be a regular part of the language API. 37 | 38 | For example: 39 | 40 | ```js 41 | $ ./node 42 | > util.isDate(new Date()) 43 | true 44 | > util.isDate(vm.runInNewContext('new Date()')) 45 | true 46 | > vm.runInNewContext('new Date()') instanceof Date 47 | false 48 | ``` 49 | 50 | ## Requirements 51 | 52 | What is needed? 53 | 54 | * Mechanism for reliably determining if any given object is a built-in or is an 55 | instance of a built-in, even across realms. 56 | * Mechanism for reliably determining if objects from different realms correspond 57 | to the same built-in (e.g. `Date` from one realm is the same built-in as 58 | `Date` from a second realm). 59 | * Avoid introducing new, or changing existing, language syntax. 60 | * Allow host environments to insert new built-ins. 61 | * Allow user code objects to masquerade as built-ins. 62 | 63 | ## Proposed API 64 | 65 | ### Identifying an Object as a Built-in 66 | 67 | An object is identified as a built-in using: 68 | 69 | * A new `[[Builtin]]` internal slot to mark built-ins 70 | * A new `@@builtin` symbol (`Symbol.builtin`) property whose value is a function 71 | whose default behavior is to provide the value of the `[[Builtin]]` internal 72 | slot. 73 | 74 | #### `[[Builtin]]` internal slot 75 | 76 | Intrinsic objects listed in the table below have a `[[Builtin]]` internal slot 77 | with the given string value. Intrinsic objects not listed in the table do *not* 78 | have the `[[Builtin]]` internal slot. 79 | 80 | | Intrinsic Name | Builtin Name | 81 | | --------------------- | --------------------- | 82 | | `%Array%` | `'Array'` | 83 | | `%ArrayBuffer%` | `'ArrayBuffer'` | 84 | | `%AsyncFunction%` | `'AsyncFunction'` | 85 | | `%Atomics%` | `'Atomics'` | 86 | | `%Boolean%` | `'Boolean'` | 87 | | `%DataView%` | `'DataView'` | 88 | | `%Date%` | `'Date'` | 89 | | `%Error%` | `'Error'` | 90 | | `%EvalError%` | `'EvalError'` | 91 | | `%Float32Array%` | `'Float32Array'` | 92 | | `%Float64Array%` | `'Float64Array'` | 93 | | `%Function%` | `'function'` | 94 | | `%GeneratorFunction%` | `'GeneratorFunction'` | 95 | | `%Int8Array%` | `'Int8Array'` | 96 | | `%Int16Array%` | `'Int16Array'` | 97 | | `%Int32Array%` | `'Int32Array'` | 98 | | `%JSON%` | `'JSON'` | 99 | | `%Map%` | `'Map'` | 100 | | `%Math%` | `'Math'` | 101 | | `%Number%` | `'Number'` | 102 | | `%Object%` | `'object'` | 103 | | `%Promise%` | `'Promise'` | 104 | | `%Proxy%` | `'Proxy'` | 105 | | `%RangeError%` | `'RangeError'` | 106 | | `%ReferenceError%` | `'ReferenceError'` | 107 | | `%Reflect%` | `'Reflect'` | 108 | | `%RegExp%` | `'RegExp'` | 109 | | `%Set%` | `'Set'` | 110 | | `%SharedArrayBuffer%` | `'SharedArrayBuffer'` | 111 | | `%String%` | `'String'` | 112 | | `%Symbol%` | `'symbol'` | 113 | | `%SyntaxError%` | `'SyntaxError'` | 114 | | `%TypeError%` | `'TypeError'` | 115 | | `%Uint8Array%` | `'Uint8Array'` | 116 | | `%Uint8ClampedArray%` | `'Uint8ClampedArray'` | 117 | | `%Uint16Array%` | `'Uint16Array'` | 118 | | `%Uint32Array%` | `'Uint32Array'` | 119 | | `%URIError%` | `'URIError'` | 120 | | `%WeakMap%` | `'WeakMap'` | 121 | | `%WeakSet%` | `'WeakSet'` | 122 | 123 | *Note*: Currently, intrinsic prototype objects such as `%DatePrototype%` 124 | intentionally do *not* have a `[[Builtin]]` internal slot. The effect of this 125 | is such that `Builtin.typeOf(new Date())` would return `'Date'`, 126 | `Builtin.typeOf(Object.getPrototypeOf(new Date()))` would return `'object'`, 127 | despite `%DatePrototype%` being an intrinsic object. The justification for 128 | this is that it is not yet clear if intrinsic prototype objects *need* to be 129 | identifiable as built-ins. 130 | 131 | In addition, all built-in non-constructor functions and methods have a 132 | `[[Builtin]]` internal slot equal to the name of the function. These are used 133 | to allow using `Builtin.is()` to determine if two function/method instances 134 | represent the same intrinsic function or method. 135 | 136 | For instance, 137 | 138 | ```js 139 | Builtin.is(eval, vm.runInNewContext('eval')); // true 140 | Builtin.is(Object.prototype.toString, 141 | vm.runInNewContext('Object.prototype.toString')); // true 142 | ``` 143 | 144 | #### `Symbol.builtin` 145 | 146 | The initial value of the `@@builtin` own property for all intrinsic objects 147 | having a `[[Builtin]]` internal slot is the same function that returns the 148 | value of the `[[Builtin]]` internal slot. Intrinsic objects that do not have 149 | the `[[Builtin]]` internal slot do not have an initial value for the `@@builtin` 150 | own property. 151 | 152 | ```js 153 | const builtIn1 = Date[Symbol.builtin]; 154 | const builtIn2 = Uint8Array[Symbol.builtin]; 155 | const same = builtIn1 === builtIn2; // true 156 | ``` 157 | 158 | An object is detectable as a built-in if it has the `@@builtin` own property. 159 | 160 | An object is detectable as an *instance* of a built-in if its constructor has a 161 | `@@builtin` property as either an own or inherited property. 162 | 163 | ```js 164 | class Foo { 165 | static [Symbol.builtin]() { 166 | return 'Foo'; 167 | } 168 | } 169 | class Bar extends Foo {} 170 | 171 | Builtin.typeOf(new Foo()); // 'Foo' 172 | 173 | Builtin.typeOf(new Bar()); // 'Foo' 174 | ``` 175 | 176 | Setting the `@@builtin` property to a non-function value makes the object, 177 | or instances of the object, no longer detectable as built-ins: 178 | 179 | ```js 180 | Builtin.typeOf(new Uint8Array(0)); // 'Uint8Array' 181 | 182 | Uint8Array[Symbol.builtin] = undefined; 183 | 184 | Builtin.typeOf(new Uint8Array(0)); // 'object' 185 | ``` 186 | 187 | The `@@builtin` property has the attributes: 188 | 189 | * `[[Configurable]]: true` 190 | * `[[Enumerable]]: false` 191 | * `[[Writable]]: true` 192 | 193 | ### Abstract Operations 194 | 195 | #### `GetBuiltinValue` 196 | 197 | The abstract operation `GetBuiltinValue` with argument `object` performs the 198 | following steps: 199 | 200 | * Let `fn` be `? GetMethod(object, @@builtin)`. 201 | * If `fn` is `undefined`, return `undefined`. 202 | * Let `value` be `? Call(fn, object)`. 203 | * If `value` is `undefined`, return `undefined`. 204 | * Return `? ToString(value)`. 205 | 206 | #### `GetOwnBuiltinValue` 207 | 208 | The abstract operation `GetOwnBuiltinValue` with argument `object` performs the 209 | following steps: 210 | 211 | * Let `hasProperty` be `? HasOwnProperty(object, @@builtin)`. 212 | * If `hasProperty` is `false`, return `undefined`. 213 | * Return `? GetBuiltinValue(object)`. 214 | 215 | ### `Builtin` 216 | 217 | The `Builtin` object is the `%Builtin%` intrinsic object and the initial 218 | value of the `Builtin` property of the `global` object. The `Builtin` 219 | object is an ordinary object. 220 | 221 | The value of the `[[Prototype]]` internal slot of the `Builtin` object is 222 | the intrinsic object `%ObjectPrototype%`. 223 | 224 | The `Builtin` object is not a function object. It does not have a 225 | `[[Construct]]` internal method; it is not possible to use the `Builtin` 226 | object as a constructor with the `new` operator. The `Builtin` object also 227 | does not have a `[[Call]]` internal method; it is not possible to invoke the 228 | `Builtin` object as a function. 229 | 230 | #### `Builtin.is(value1, value2)` 231 | 232 | When called with arguments `value1` and `value2`: 233 | 234 | * If `Type(value1)` is not `Object` return `false`. 235 | * Let `V1` be `? GetOwnBuiltinValue(value1)`. 236 | * If `V1` is `undefined`, return `false`. 237 | * If `value2` is `undefined`, return `false`. 238 | * If `Type(value2)` is not `Object`, return `false`. 239 | * Let `V2` be `? GetOwnBuiltinValue(value2)`. 240 | * Let `same` be the result of performing Strict Equality Comparison `V1 === V2`. 241 | * Return `same` 242 | 243 | The `Builtin.is()` function returns `true` if both of the given values have a 244 | `@@builtin` own property function that each returns values that, after coercion 245 | to a string, are strictly equal to one another. Otherwise, return `false`. 246 | 247 | ```js 248 | Builtin.is(Date, vm.runInNewContext('Date')); // true 249 | Builtin.is(Date, vm.runInNewContext('Number')); // false 250 | Builtin.is(Date, vm.runInNewContext('{}')); // false 251 | Builtin.is({}, vm.runInNewContext('{}')); // false 252 | 253 | Date = {}; 254 | Builtin.is(Date, vm.runInNewContext('Date')); // false 255 | ``` 256 | 257 | Note that user code may modify the `@@builtin` own property on any object: 258 | 259 | ```js 260 | Date[Symbol.builtin] = undefined; 261 | Builtin.is(Date, vm.runInNewContext('Date')); // false 262 | ``` 263 | 264 | By default, the `Builtin.is()` function will not throw an exception. It is 265 | possible for `Builtin.is()` to throw if a user-provided `@@builtin` function 266 | throws or returns a value that cannot be coerced to a string (e.g. `Symbol` 267 | values). 268 | 269 | #### `Builtin.typeOf(arg)` 270 | 271 | When the `typeOf()` function is called with argument `arg`: 272 | 273 | * If `Type(arg)` is `Object`, then: 274 | * Let `C` be `? Get(arg, "constructor")`. 275 | * If `C` is not `undefined`, then: 276 | * Let `V` be `? GetBuiltinValue(C)`. 277 | * If `V` is not `undefined`, return `V`. 278 | * Return `typeof arg`. 279 | 280 | For example: 281 | 282 | ```js 283 | Builtin.typeOf([]); // 'Array' 284 | Builtin.typeOf(new ArrayBuffer()); // 'ArrayBuffer' 285 | Builtin.typeOf(async function foo() {}); // 'AsyncFunction' 286 | Builtin.typeOf(new Boolean()); // 'Boolean' 287 | Builtin.typeOf(new DataView(buffer)); // 'DataView' 288 | Builtin.typeOf(new Date()); // 'Date' 289 | Builtin.typeOf(new Error()); // 'Error' 290 | Builtin.typeOf(new EvalError()); // 'EvalError' 291 | Builtin.typeOf(new Float32Array()); // 'Float32Array' 292 | Builtin.typeOf(new Float64Array()); // 'Float64Array' 293 | Builtin.typeOf(function() {}); // 'function' 294 | Builtin.typeOf(function*() {}); // 'GeneratorFunction' 295 | Builtin.typeOf(new Int16Array()); // 'Int16Array' 296 | Builtin.typeOf(new Int32Array()); // 'Int32Array' 297 | Builtin.typeOf(new Int8Array()); // 'Int8Array' 298 | Builtin.typeOf(new InternalError()); // 'InternalError' 299 | Builtin.typeOf(new Intl.Collator()); // 'Collator' 300 | Builtin.typeOf(new Intl.DateTimeFormat()); // 'DateTimeFormat' 301 | Builtin.typeOf(new Intl.NumberFormat()); // 'NumberFormat' 302 | Builtin.typeOf(new Map()); // 'Map' 303 | Builtin.typeOf(new Number()); // 'Number' 304 | Builtin.typeOf(new Object()); // 'object' 305 | Builtin.typeOf(new Promise(() => {})); // 'Promise' 306 | Builtin.typeOf(new RangeError()); // 'RangeError' 307 | Builtin.typeOf(new ReferenceError()); // 'ReferenceError' 308 | Builtin.typeOf(new RegExp('')); // 'RegExp' 309 | Builtin.typeOf(new Set()); // 'Set' 310 | Builtin.typeOf(new SharedArrayBuffer()); // 'SharedArrayBuffer' 311 | Builtin.typeOf(new String()); // 'String' 312 | Builtin.typeOf(new SyntaxError()); // 'SyntaxError' 313 | Builtin.typeOf(new TypeError()); // 'TypeError' 314 | Builtin.typeOf(new URIError()); // 'URIError' 315 | Builtin.typeOf(new Uint16Array()); // 'Uint16Array' 316 | Builtin.typeOf(new Uint32Array()); // 'Uint32Array' 317 | Builtin.typeOf(new Uint8Array()); // 'Uint8Array' 318 | Builtin.typeOf(new Uint8ClampedArray()); // 'Uint8ClampedArray' 319 | Builtin.typeOf(new WeakMap()); // 'WeakMap' 320 | Builtin.typeOf(new WeakSet()); // 'WeatSet' 321 | Builtin.typeOf(new WebAssembly.Module()); // 'Module' 322 | Builtin.typeOf(new WebAssembly.Instance()); // 'Instance' 323 | Builtin.typeOf(new WebAssembly.Memory()); // 'Memory' 324 | Builtin.typeOf(new WebAssembly.Table()); // 'Table' 325 | Builtin.typeOf(new WebAssembly.CompileError()); // 'CompileError' 326 | Builtin.typeOf(new WebAssembly.LinkError()); // 'LinkError' 327 | Builtin.typeOf(new WebAssembly.RuntimeError()); // 'RuntimeError' 328 | Builtin.typeOf(null); // 'null' 329 | Builtin.typeOf(undefined); // 'undefined' 330 | Builtin.typeOf({}); // 'object' 331 | Builtin.typeOf(true); // 'boolean' 332 | Builtin.typeOf(1); // 'number' 333 | Builtin.typeOf('test'); // 'string' 334 | Builtin.typeOf(Symbol('foo')); // 'symbol' 335 | Builtin.typeOf(function() {}); // 'function' 336 | 337 | 338 | class MyArray extends Uint8Array {} 339 | const myArray = new MyArray(); 340 | Builtin.typeOf(myArray); // 'Uint8Array' 341 | 342 | vm.runInNewContext('Builtin.typeOf(myArray)', { myArray }); // 'Uint8Array' 343 | ``` 344 | 345 | By default, the `Builtin.typeOf()` function will not throw an exception. It is 346 | possible for `Builtin.typeOf()` to throw if a user-provided `@@builtin` function 347 | throws or returns a value that cannot be coerced to a string (e.g. `Symbol` 348 | values). 349 | 350 | *Note*: Because of the nature of `Proxy` instances, it is not possible for 351 | `Builtin.typeOf(proxyObj)` to ever return `'Proxy'`. 352 | 353 | ### `Proxy.isProxy(value)` 354 | 355 | Returns `true` if `value` is a Proxy exotic object, otherwise return `false`. 356 | 357 | The `Proxy.isProxy()` function will not throw an exception. 358 | 359 | *Note*: Due to the security issues around `Proxy`, host environments should be 360 | allowed to provide an option for forcing `Proxy.isProxy(value)` to always 361 | return `false`. For instance, Node.js could hypothetically provide a 362 | command-line argument like `--disable-isproxy`. 363 | 364 | ### Notes 365 | 366 | * Adding a new `%Builtin%` intrinsic object can be avoided by adding functions 367 | to an existing intrinisic, for instance `Object.isBuiltin()` or 368 | `Object.typeOf()`. 369 | 370 | * Using `@@builtin` means that any object can lie about being a built-in by 371 | setting the `@@builtin` own property to whatever value it wants. This is by 372 | design. Polyfills/shims and secure-realm code, for example, must be able to 373 | create builtins, remove them, or replace builtins that are noncompliant - as 374 | such, a shim (that runs before other code) must be able to create its own 375 | builtin replacement and truly masquerade as if it were the original builtin. 376 | 377 | * Why have a separate `Proxy.isProxy()` function? For the simple reason that 378 | `Proxy` objects do not act like anything else. The use case justifying 379 | `Proxy.isProxy()` is that, when debugging, it can often be necessary 380 | to know if the an object of interest is a Proxy or not. 381 | 382 | * The `Builtin` property on the `global` object is set initially to the 383 | `Builtin` object. This property has the attributes: 384 | * `[[Configurable]]: true` 385 | * `[[Enumerable]]: true` 386 | * `[[Writable]]: true` 387 | 388 | * The `Builtin.is`, `Builtin.typeOf`, and `Proxy.isProxy` properties have 389 | the attributes: 390 | * `[[Configurable]]: true` 391 | * `[[Enumerable]]: true` 392 | * `[[Writable]]: true` 393 | 394 | ## Examples 395 | 396 | ```js 397 | function formatValue(value) { 398 | switch (Builtin.typeOf(value)) { 399 | case 'Date': 400 | return formatDate(value); 401 | case 'Array': 402 | return formatArray(value); 403 | case 'RegExp': 404 | return formatRegExp(value); 405 | /** ... **/ 406 | } 407 | } 408 | ``` 409 | 410 | ```js 411 | const val = vm.runInNewContext('Date'); 412 | if (Builtin.is(val, Date)) { 413 | /** ... **/ 414 | } else if (Builtin.is(val, Math)) { 415 | /** ... **/ 416 | } 417 | ``` 418 | 419 | Because the value of `@@builtin` is a function, the original implementation can 420 | be captured, cached, and restored later: 421 | 422 | ```js 423 | const origDateBuiltin = Date[Symbol.builtin]; 424 | Date[Symbol.builtin] = undefined; 425 | 426 | Builtin.is(Date, vm.runInNewContext('Date')); // false 427 | Builtin.typeOf(Date); // 'object' 428 | 429 | origDateBuiltin.call(Date); // 'Date' 430 | 431 | Date[Symbol.builtin] = origDateBuiltin; 432 | 433 | Builtin.is(Date, vm.runInNewContext('Date')); // true 434 | Builtin.typeOf(Date); // 'Date' 435 | ``` 436 | 437 | *Note*: The behavior of the initial `@@builtin` function is to return the value 438 | of the `this` objects `[[Builtin]]` internal slot if one exists. Accordingly, 439 | it is possible to grab a reference to the function once and use it on multiple 440 | objects: 441 | 442 | ```js 443 | const origBuiltin = Date[Symbol.builtin]; 444 | Uint8Array[Symbol.builtin] = origBuiltin; 445 | 446 | class Foo {} 447 | Foo[Symbol.builtin] = origBuiltin; 448 | 449 | Date[Symbol.builtin](); // 'Date' 450 | Uint8Array[Symbol.builtin](); // 'Uint8Array' 451 | Foo[Symbol.builtin](); // undefined 452 | 453 | Builtin.is(Date, vm.runInNewContext('Date')); // true 454 | Builtin.is(Uint8Array, vm.runInNewContext('Uin8Array')); // true 455 | 456 | Builtin.typeOf(new Date()); // 'Date' 457 | Builtin.typeOf(new Uint8Array()); // 'Uint8Array' 458 | Builtin.typeOf(new Foo()); // 'object' 459 | ``` 460 | --------------------------------------------------------------------------------