@@ -7,6 +7,7 @@ description: |
77defines:
88 - verifyProperty
99 - verifyCallableProperty
10+ - verifyAccessorProperty
1011 - verifyEqualTo # deprecated
1112 - verifyWritable # deprecated
1213 - verifyNotWritable # deprecated
@@ -16,6 +17,7 @@ defines:
1617 - verifyNotConfigurable # deprecated
1718 - verifyPrimordialProperty
1819 - verifyPrimordialCallableProperty
20+ - verifyPrimordialAccessorProperty
1921---*/
2022
2123// @ts -check
@@ -37,33 +39,31 @@ var nonIndexNumericPropertyName = Math.pow(2, 32) - 1;
3739 * @param {string|symbol } name
3840 * @param {PropertyDescriptor|undefined } desc
3941 * @param {object } [options]
42+ * @param {boolean } [options.label]
4043 * @param {boolean } [options.restore] revert mutations from verifying writable/configurable
4144 */
4245function verifyProperty ( obj , name , desc , options ) {
4346 assert (
4447 arguments . length > 2 ,
4548 'verifyProperty should receive at least 3 arguments: obj, name, and descriptor'
4649 ) ;
50+ var label = options && options . label || String ( name ) ;
4751
4852 var originalDesc = __getOwnPropertyDescriptor ( obj , name ) ;
49- var nameStr = String ( name ) ;
5053
5154 // Allows checking for undefined descriptor if it's explicitly given.
5255 if ( desc === undefined ) {
5356 assert . sameValue (
5457 originalDesc ,
5558 undefined ,
56- "obj['" + nameStr + "'] descriptor should be undefined"
59+ label + " descriptor should be undefined"
5760 ) ;
5861
5962 // desc and originalDesc are both undefined, problem solved;
6063 return true ;
6164 }
6265
63- assert (
64- __hasOwnProperty ( obj , name ) ,
65- "obj should have an own property " + nameStr
66- ) ;
66+ assert ( __hasOwnProperty ( obj , name ) , label + " should be an own property" ) ;
6767
6868 assert . notSameValue (
6969 desc ,
@@ -94,17 +94,17 @@ function verifyProperty(obj, name, desc, options) {
9494
9595 if ( __hasOwnProperty ( desc , 'value' ) ) {
9696 if ( ! isSameValue ( desc . value , originalDesc . value ) ) {
97- __push ( failures , "obj['" + nameStr + "'] descriptor value should be " + desc . value ) ;
97+ __push ( failures , label + " descriptor value should be " + String ( desc . value ) ) ;
9898 }
9999 if ( ! isSameValue ( desc . value , obj [ name ] ) ) {
100- __push ( failures , "obj['" + nameStr + "'] value should be " + desc . value ) ;
100+ __push ( failures , label + " value should be " + String ( desc . value ) ) ;
101101 }
102102 }
103103
104104 if ( __hasOwnProperty ( desc , 'enumerable' ) && desc . enumerable !== undefined ) {
105105 if ( desc . enumerable !== originalDesc . enumerable ||
106106 desc . enumerable !== isEnumerable ( obj , name ) ) {
107- __push ( failures , "obj['" + nameStr + "'] descriptor should " + ( desc . enumerable ? '' : 'not ' ) + "be enumerable" ) ;
107+ __push ( failures , label + " descriptor should " + ( desc . enumerable ? '' : 'not ' ) + "be enumerable" ) ;
108108 }
109109 }
110110
@@ -113,14 +113,14 @@ function verifyProperty(obj, name, desc, options) {
113113 if ( __hasOwnProperty ( desc , 'writable' ) && desc . writable !== undefined ) {
114114 if ( desc . writable !== originalDesc . writable ||
115115 desc . writable !== isWritable ( obj , name ) ) {
116- __push ( failures , "obj['" + nameStr + "'] descriptor should " + ( desc . writable ? '' : 'not ' ) + "be writable" ) ;
116+ __push ( failures , label + " descriptor should " + ( desc . writable ? '' : 'not ' ) + "be writable" ) ;
117117 }
118118 }
119119
120120 if ( __hasOwnProperty ( desc , 'configurable' ) && desc . configurable !== undefined ) {
121121 if ( desc . configurable !== originalDesc . configurable ||
122122 desc . configurable !== isConfigurable ( obj , name ) ) {
123- __push ( failures , "obj['" + nameStr + "'] descriptor should " + ( desc . configurable ? '' : 'not ' ) + "be configurable" ) ;
123+ __push ( failures , label + " descriptor should " + ( desc . configurable ? '' : 'not ' ) + "be configurable" ) ;
124124 }
125125 }
126126
@@ -219,13 +219,17 @@ function isWritable(obj, name, verifyProp, value) {
219219 * @param {number } functionLength
220220 * @param {PropertyDescriptor } [desc] defaults to data property conventions (writable, non-enumerable, configurable)
221221 * @param {object } [options]
222+ * @param {boolean } [options.label]
223+ * @param {typeof verifyProperty } [options.verifyProperty]
222224 * @param {boolean } [options.restore] revert mutations from verifying writable/configurable
223225 */
224226function verifyCallableProperty ( obj , name , functionName , functionLength , desc , options ) {
225- var value = obj [ name ] ;
227+ var label = options && options . label || String ( name ) ;
228+ var propertyVerifier = options && options . verifyProperty || verifyProperty ;
229+
230+ var value = obj && obj [ name ] ;
226231
227- assert . sameValue ( typeof value , "function" ,
228- "obj['" + String ( name ) + "'] descriptor should be a function" ) ;
232+ assert . sameValue ( typeof value , "function" , label + " should be a function" ) ;
229233
230234 // Every other data property described in clauses 19 through 28 and in
231235 // Annex B.2 has the attributes { [[Writable]]: true, [[Enumerable]]: false,
@@ -242,7 +246,7 @@ function verifyCallableProperty(obj, name, functionName, functionLength, desc, o
242246 desc . value = value ;
243247 }
244248
245- verifyProperty ( obj , name , desc , options ) ;
249+ propertyVerifier ( obj , name , desc , options ) ;
246250
247251 if ( functionName === undefined ) {
248252 if ( typeof name === "symbol" ) {
@@ -256,24 +260,125 @@ function verifyCallableProperty(obj, name, functionName, functionLength, desc, o
256260 // [[Configurable]]: true }.
257261 // https://tc39.es/ecma262/multipage/ecmascript-standard-built-in-objects.html#sec-ecmascript-standard-built-in-objects
258262 // https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-setfunctionname
259- verifyProperty ( value , "name" , {
263+ propertyVerifier ( value , "name" , {
260264 value : functionName ,
261265 writable : false ,
262266 enumerable : false ,
263267 configurable : desc . configurable
264- } , options ) ;
268+ } , { label : label + " name" , restore : options && options . restore } ) ;
265269
266270 // Unless otherwise specified, the "length" property of a built-in function
267271 // object has the attributes { [[Writable]]: false, [[Enumerable]]: false,
268272 // [[Configurable]]: true }.
269273 // https://tc39.es/ecma262/multipage/ecmascript-standard-built-in-objects.html#sec-ecmascript-standard-built-in-objects
270274 // https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-setfunctionlength
271- verifyProperty ( value , "length" , {
275+ propertyVerifier ( value , "length" , {
272276 value : functionLength ,
273277 writable : false ,
274278 enumerable : false ,
275279 configurable : desc . configurable
276- } , options ) ;
280+ } , { label : label + " length" , restore : options && options . restore } ) ;
281+ }
282+
283+ /**
284+ * Verify that there is an accessor property associated with `obj[name]` and
285+ * following the conventions for built-in objects.
286+ *
287+ * @param {object } obj
288+ * @param {string|symbol } name
289+ * @param {object } desc
290+ * @param {boolean } [desc.enumerable] defaults to accessor property conventions (non-enumerable)
291+ * @param {boolean } [desc.configurable] defaults to accessor property conventions (configurable)
292+ * @param {undefined | Function | {name?: string|symbol, length?: number} } [desc.get] if an object,
293+ * absent fields default to getter conventions (name derived from the property key with a "get "
294+ * prefix, length 0)
295+ * @param {undefined | Function | {name?: string|symbol, length?: number} } [desc.set] if an object,
296+ * absent fields default to getter conventions (name derived from the property key with a "set "
297+ * prefix, length 1)
298+ * @param {object } [options]
299+ * @param {boolean } [options.label]
300+ * @param {typeof verifyProperty } [options.verifyProperty]
301+ * @param {typeof verifyCallableProperty } [options.verifyCallableProperty]
302+ * @param {boolean } [options.restore] revert mutations from verifying property attributes
303+ */
304+ function verifyAccessorProperty ( obj , name , desc , options ) {
305+ var checkGet = __hasOwnProperty ( desc , "get" ) ;
306+ var checkSet = __hasOwnProperty ( desc , "set" ) ;
307+ assert (
308+ checkGet || checkSet ,
309+ 'verifyAccessorProperty requires at least one of "get" and "set"'
310+ ) ;
311+ var label = options && options . label || String ( name ) ;
312+ var propertyVerifier = options && options . verifyProperty || verifyProperty ;
313+ var callabilityVerifier = options && options . verifyCallableProperty || verifyCallableProperty ;
314+
315+ var originalDesc = __getOwnPropertyDescriptor ( obj , name ) ;
316+
317+ // Every built-in function object, including constructors, has a "name"
318+ // property whose value is a String. Unless otherwise specified, this value is
319+ // the name that is given to the function in this specification. Functions
320+ // that are identified as anonymous functions use the empty String as the
321+ // value of the "name" property. For functions that are specified as
322+ // properties of objects, the name value is the property name string used to
323+ // access the function. Functions that are specified as get or set accessor
324+ // functions of built-in properties have "get" or "set" (respectively) passed
325+ // to the prefix parameter when calling CreateBuiltinFunction.
326+ //
327+ // The value of the "name" property is explicitly specified for each built-in
328+ // functions whose property key is a Symbol value. If such an explicitly
329+ // specified value starts with the prefix "get " or "set " and the function
330+ // for which it is specified is a get or set accessor function of a built-in
331+ // property, the value without the prefix is passed to the name parameter, and
332+ // the value "get" or "set" (respectively) is passed to the prefix parameter
333+ // when calling CreateBuiltinFunction.
334+ // https://tc39.es/ecma262/multipage/ecmascript-standard-built-in-objects.html
335+ if ( checkGet ) {
336+ var expectGetter = desc . get ;
337+ var getterLabel = label + " getter" ;
338+ if ( expectGetter === undefined || typeof expectGetter === "function" ) {
339+ assert . sameValue ( originalDesc . get , expectGetter , getterLabel ) ;
340+ } else {
341+ var getterName = expectGetter . name ;
342+ if ( getterName === undefined ) {
343+ getterName = "get " + ( typeof name === "symbol" ? "[" + name . description + "]" : name ) ;
344+ }
345+ var getterLength = expectGetter . length !== undefined ? expectGetter . length : 0 ;
346+ var getterOptions = { label : getterLabel } ;
347+ callabilityVerifier ( originalDesc , "get" , getterName , getterLength , { } , getterOptions ) ;
348+ }
349+ }
350+ if ( checkSet ) {
351+ var expectSetter = desc . set ;
352+ var setterLabel = label + " setter" ;
353+ if ( expectSetter === undefined || typeof expectSetter === "function" ) {
354+ assert . sameValue ( originalDesc . set , expectSetter , setterLabel ) ;
355+ } else {
356+ var setterName = expectSetter . name ;
357+ if ( setterName === undefined ) {
358+ setterName = "set " + ( typeof name === "symbol" ? "[" + name . description + "]" : name ) ;
359+ }
360+ var setterLength = expectSetter . length !== undefined ? expectSetter . length : 1 ;
361+ var setterOptions = { label : setterLabel } ;
362+ callabilityVerifier ( originalDesc , "set" , setterName , setterLength , { } , setterOptions ) ;
363+ }
364+ }
365+
366+ // Every accessor property described in clauses 19 through 28 and in Annex B.2
367+ // has the attributes { [[Enumerable]]: false, [[Configurable]]: true } unless
368+ // otherwise specified.
369+ // https://tc39.es/ecma262/multipage/ecmascript-standard-built-in-objects.html
370+ var resolvedDesc = { get : originalDesc . get , set : originalDesc . set } ;
371+ if ( ! __hasOwnProperty ( desc , "enumerable" ) ) {
372+ resolvedDesc . enumerable = false ;
373+ } else if ( desc . enumerable !== undefined ) {
374+ resolvedDesc . enumerable = desc . enumerable ;
375+ }
376+ if ( ! __hasOwnProperty ( desc , "configurable" ) ) {
377+ resolvedDesc . configurable = true ;
378+ } else if ( desc . configurable !== undefined ) {
379+ resolvedDesc . configurable = desc . configurable ;
380+ }
381+ propertyVerifier ( obj , name , resolvedDesc , options ) ;
277382}
278383
279384/**
@@ -367,5 +472,39 @@ var verifyPrimordialProperty = verifyProperty;
367472 * Use this function to verify the primordial function-valued properties.
368473 * For non-primordial functions, use verifyCallableProperty.
369474 * See: https://github.com/tc39/how-we-work/blob/main/terminology.md#primordial
475+ *
476+ * @type {typeof verifyCallableProperty }
370477 */
371- var verifyPrimordialCallableProperty = verifyCallableProperty ;
478+ function verifyPrimordialCallableProperty ( obj , name , functionName , functionLength , desc , options ) {
479+ var resolvedOptions = {
480+ verifyProperty : options && options . verifyProperty !== undefined
481+ ? options . verifyProperty
482+ : verifyPrimordialProperty
483+ } ;
484+ if ( options && options . label !== undefined ) resolvedOptions . label = options . label ;
485+ if ( options && options . restore !== undefined ) resolvedOptions . restore = options . restore ;
486+
487+ return verifyCallableProperty ( obj , name , functionName , functionLength , desc , resolvedOptions ) ;
488+ }
489+
490+ /**
491+ * Use this function to verify the primordial accessor properties.
492+ * For non-primordial functions, use verifyAccessorProperty.
493+ * See: https://github.com/tc39/how-we-work/blob/main/terminology.md#primordial
494+ *
495+ * @type {typeof verifyAccessorProperty }
496+ */
497+ function verifyPrimordialAccessorProperty ( obj , name , desc , options ) {
498+ var resolvedOptions = {
499+ verifyProperty : options && options . verifyProperty !== undefined
500+ ? options . verifyProperty
501+ : verifyPrimordialProperty ,
502+ verifyCallableProperty : options && options . verifyCallableProperty !== undefined
503+ ? options . verifyCallableProperty
504+ : verifyPrimordialCallableProperty
505+ } ;
506+ if ( options && options . label !== undefined ) resolvedOptions . label = options . label ;
507+ if ( options && options . restore !== undefined ) resolvedOptions . restore = options . restore ;
508+
509+ return verifyAccessorProperty ( obj , name , desc , resolvedOptions ) ;
510+ }
0 commit comments