harness: Add a verifyCallableProperty helper (#4468)

This commit is contained in:
Richard Gibson 2025-05-14 12:08:11 -04:00 committed by GitHub
parent 5e201642db
commit 25278a150d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 85 additions and 302 deletions

View File

@ -6,6 +6,7 @@ description: |
property descriptors.
defines:
- verifyProperty
- verifyCallableProperty
- verifyEqualTo # deprecated
- verifyWritable # deprecated
- verifyNotWritable # deprecated
@ -14,6 +15,7 @@ defines:
- verifyConfigurable # deprecated
- verifyNotConfigurable # deprecated
- verifyPrimordialProperty
- verifyPrimordialCallableProperty
---*/
// @ts-check
@ -22,6 +24,8 @@ defines:
// are used in verification but might be destroyed *by* that process itself.
var __isArray = Array.isArray;
var __defineProperty = Object.defineProperty;
var __getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
var __getOwnPropertyNames = Object.getOwnPropertyNames;
var __join = Function.prototype.call.bind(Array.prototype.join);
var __push = Function.prototype.call.bind(Array.prototype.push);
var __hasOwnProperty = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
@ -33,7 +37,7 @@ var nonIndexNumericPropertyName = Math.pow(2, 32) - 1;
* @param {string|symbol} name
* @param {PropertyDescriptor|undefined} desc
* @param {object} [options]
* @param {boolean} [options.restore]
* @param {boolean} [options.restore] revert mutations from verifying writable/configurable
*/
function verifyProperty(obj, name, desc, options) {
assert(
@ -41,7 +45,7 @@ function verifyProperty(obj, name, desc, options) {
'verifyProperty should receive at least 3 arguments: obj, name, and descriptor'
);
var originalDesc = Object.getOwnPropertyDescriptor(obj, name);
var originalDesc = __getOwnPropertyDescriptor(obj, name);
var nameStr = String(name);
// Allows checking for undefined descriptor if it's explicitly given.
@ -73,7 +77,7 @@ function verifyProperty(obj, name, desc, options) {
"The desc argument should be an object or undefined, " + String(desc)
);
var names = Object.getOwnPropertyNames(desc);
var names = __getOwnPropertyNames(desc);
for (var i = 0; i < names.length; i++) {
assert(
names[i] === "value" ||
@ -90,37 +94,39 @@ function verifyProperty(obj, name, desc, options) {
if (__hasOwnProperty(desc, 'value')) {
if (!isSameValue(desc.value, originalDesc.value)) {
__push(failures, "descriptor value should be " + desc.value);
__push(failures, "obj['" + nameStr + "'] descriptor value should be " + desc.value);
}
if (!isSameValue(desc.value, obj[name])) {
__push(failures, "object value should be " + desc.value);
__push(failures, "obj['" + nameStr + "'] value should be " + desc.value);
}
}
if (__hasOwnProperty(desc, 'enumerable')) {
if (__hasOwnProperty(desc, 'enumerable') && desc.enumerable !== undefined) {
if (desc.enumerable !== originalDesc.enumerable ||
desc.enumerable !== isEnumerable(obj, name)) {
__push(failures, 'descriptor should ' + (desc.enumerable ? '' : 'not ') + 'be enumerable');
__push(failures, "obj['" + nameStr + "'] descriptor should " + (desc.enumerable ? '' : 'not ') + "be enumerable");
}
}
// Operations past this point are potentially destructive!
if (__hasOwnProperty(desc, 'writable')) {
if (__hasOwnProperty(desc, 'writable') && desc.writable !== undefined) {
if (desc.writable !== originalDesc.writable ||
desc.writable !== isWritable(obj, name)) {
__push(failures, 'descriptor should ' + (desc.writable ? '' : 'not ') + 'be writable');
__push(failures, "obj['" + nameStr + "'] descriptor should " + (desc.writable ? '' : 'not ') + "be writable");
}
}
if (__hasOwnProperty(desc, 'configurable')) {
if (__hasOwnProperty(desc, 'configurable') && desc.configurable !== undefined) {
if (desc.configurable !== originalDesc.configurable ||
desc.configurable !== isConfigurable(obj, name)) {
__push(failures, 'descriptor should ' + (desc.configurable ? '' : 'not ') + 'be configurable');
__push(failures, "obj['" + nameStr + "'] descriptor should " + (desc.configurable ? '' : 'not ') + "be configurable");
}
}
assert(!failures.length, __join(failures, '; '));
if (failures.length) {
assert(false, __join(failures, '; '));
}
if (options && options.restore) {
__defineProperty(obj, name, originalDesc);
@ -202,6 +208,56 @@ function isWritable(obj, name, verifyProp, value) {
return writeSucceeded;
}
/**
* @param {object} obj
* @param {string|symbol} name
* @param {string} [functionName] defaults to name for strings, `[${name.description}]` for symbols
* @param {number} functionLength
* @param {PropertyDescriptor} desc
* @param {object} [options]
* @param {boolean} [options.restore] revert mutations from verifying writable/configurable
*/
function verifyCallableProperty(obj, name, functionName, functionLength, desc, options) {
var value = obj[name];
assert.sameValue(typeof value, "function",
"obj['" + String(name) + "'] descriptor should be a function");
if (!__hasOwnProperty(desc, "value")) desc.value = value;
verifyProperty(obj, name, desc, options);
if (functionName === undefined) {
if (typeof name === "symbol") {
functionName = "[" + name.description + "]";
} else {
functionName = name;
}
}
// Unless otherwise specified, the "name" property of a built-in function
// object has the attributes { [[Writable]]: false, [[Enumerable]]: false,
// [[Configurable]]: true }.
// https://tc39.es/ecma262/multipage/ecmascript-standard-built-in-objects.html#sec-ecmascript-standard-built-in-objects
// https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-setfunctionname
verifyProperty(value, "name", {
value: functionName,
writable: false,
enumerable: false,
configurable: desc.configurable
}, options);
// Unless otherwise specified, the "length" property of a built-in function
// object has the attributes { [[Writable]]: false, [[Enumerable]]: false,
// [[Configurable]]: true }.
// https://tc39.es/ecma262/multipage/ecmascript-standard-built-in-objects.html#sec-ecmascript-standard-built-in-objects
// https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-setfunctionlength
verifyProperty(value, "length", {
value: functionLength,
writable: false,
enumerable: false,
configurable: desc.configurable
}, options);
}
/**
* Deprecated; please use `verifyProperty` in new tests.
*/
@ -217,7 +273,7 @@ function verifyEqualTo(obj, name, value) {
*/
function verifyWritable(obj, name, verifyProp, value) {
if (!verifyProp) {
assert(Object.getOwnPropertyDescriptor(obj, name).writable,
assert(__getOwnPropertyDescriptor(obj, name).writable,
"Expected obj[" + String(name) + "] to have writable:true.");
}
if (!isWritable(obj, name, verifyProp, value)) {
@ -230,7 +286,7 @@ function verifyWritable(obj, name, verifyProp, value) {
*/
function verifyNotWritable(obj, name, verifyProp, value) {
if (!verifyProp) {
assert(!Object.getOwnPropertyDescriptor(obj, name).writable,
assert(!__getOwnPropertyDescriptor(obj, name).writable,
"Expected obj[" + String(name) + "] to have writable:false.");
}
if (isWritable(obj, name, verifyProp)) {
@ -242,7 +298,7 @@ function verifyNotWritable(obj, name, verifyProp, value) {
* Deprecated; please use `verifyProperty` in new tests.
*/
function verifyEnumerable(obj, name) {
assert(Object.getOwnPropertyDescriptor(obj, name).enumerable,
assert(__getOwnPropertyDescriptor(obj, name).enumerable,
"Expected obj[" + String(name) + "] to have enumerable:true.");
if (!isEnumerable(obj, name)) {
throw new Test262Error("Expected obj[" + String(name) + "] to be enumerable, but was not.");
@ -253,7 +309,7 @@ function verifyEnumerable(obj, name) {
* Deprecated; please use `verifyProperty` in new tests.
*/
function verifyNotEnumerable(obj, name) {
assert(!Object.getOwnPropertyDescriptor(obj, name).enumerable,
assert(!__getOwnPropertyDescriptor(obj, name).enumerable,
"Expected obj[" + String(name) + "] to have enumerable:false.");
if (isEnumerable(obj, name)) {
throw new Test262Error("Expected obj[" + String(name) + "] NOT to be enumerable, but was.");
@ -264,7 +320,7 @@ function verifyNotEnumerable(obj, name) {
* Deprecated; please use `verifyProperty` in new tests.
*/
function verifyConfigurable(obj, name) {
assert(Object.getOwnPropertyDescriptor(obj, name).configurable,
assert(__getOwnPropertyDescriptor(obj, name).configurable,
"Expected obj[" + String(name) + "] to have configurable:true.");
if (!isConfigurable(obj, name)) {
throw new Test262Error("Expected obj[" + String(name) + "] to be configurable, but was not.");
@ -275,7 +331,7 @@ function verifyConfigurable(obj, name) {
* Deprecated; please use `verifyProperty` in new tests.
*/
function verifyNotConfigurable(obj, name) {
assert(!Object.getOwnPropertyDescriptor(obj, name).configurable,
assert(!__getOwnPropertyDescriptor(obj, name).configurable,
"Expected obj[" + String(name) + "] to have configurable:false.");
if (isConfigurable(obj, name)) {
throw new Test262Error("Expected obj[" + String(name) + "] NOT to be configurable, but was.");
@ -288,3 +344,10 @@ function verifyNotConfigurable(obj, name) {
* See: https://github.com/tc39/how-we-work/blob/main/terminology.md#primordial
*/
var verifyPrimordialProperty = verifyProperty;
/**
* Use this function to verify the primordial function-valued properties.
* For non-primordial functions, use verifyCallableProperty.
* See: https://github.com/tc39/how-we-work/blob/main/terminology.md#primordial
*/
var verifyPrimordialCallableProperty = verifyCallableProperty;

View File

@ -1,16 +0,0 @@
// Copyright (C) 2016 The V8 Project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-isfinite-number
description: >
The length property of isFinite is 1
includes: [propertyHelper.js]
---*/
verifyProperty(isFinite, "length", {
value: 1,
writable: false,
enumerable: false,
configurable: true
});

View File

@ -1,27 +0,0 @@
// Copyright (C) 2015 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-isfinite-number
description: >
isFinite.name is "isFinite".
info: |
isFinite (number)
17 ECMAScript Standard Built-in Objects:
Every built-in Function object, including constructors, that is not
identified as an anonymous function has a name property whose value
is a String.
Unless otherwise specified, the name property of a built-in Function
object, if it exists, has the attributes { [[Writable]]: false,
[[Enumerable]]: false, [[Configurable]]: true }.
includes: [propertyHelper.js]
---*/
verifyProperty(isFinite, "name", {
value: "isFinite",
writable: false,
enumerable: false,
configurable: true
});

View File

@ -8,7 +8,7 @@ description: >
includes: [propertyHelper.js]
---*/
verifyProperty(this, "isFinite", {
verifyPrimordialCallableProperty(this, "isFinite", "isFinite", 1, {
writable: true,
enumerable: false,
configurable: true

View File

@ -1,16 +0,0 @@
// Copyright (C) 2016 The V8 Project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-isnan-number
description: >
The length property of isNaN is 1
includes: [propertyHelper.js]
---*/
verifyProperty(isNaN, "length", {
value: 1,
writable: false,
enumerable: false,
configurable: true
});

View File

@ -1,27 +0,0 @@
// Copyright (C) 2015 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-isnan-number
description: >
isNaN.name is "isNaN".
info: |
isNaN (number)
17 ECMAScript Standard Built-in Objects:
Every built-in Function object, including constructors, that is not
identified as an anonymous function has a name property whose value
is a String.
Unless otherwise specified, the name property of a built-in Function
object, if it exists, has the attributes { [[Writable]]: false,
[[Enumerable]]: false, [[Configurable]]: true }.
includes: [propertyHelper.js]
---*/
verifyProperty(isNaN, "name", {
value: "isNaN",
writable: false,
enumerable: false,
configurable: true
});

View File

@ -8,7 +8,7 @@ description: >
includes: [propertyHelper.js]
---*/
verifyProperty(this, "isNaN", {
verifyPrimordialCallableProperty(this, "isNaN", "isNaN", 1, {
writable: true,
enumerable: false,
configurable: true

View File

@ -1,25 +0,0 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: The length property of parseFloat has the attribute DontEnum
esid: sec-parsefloat-string
description: Checking use propertyIsEnumerable, for-in
---*/
//CHECK#1
if (parseFloat.propertyIsEnumerable('length') !== false) {
throw new Test262Error('#1: parseFloat.propertyIsEnumerable(\'length\') === false. Actual: ' + (parseFloat.propertyIsEnumerable('length')));
}
//CHECK#2
var result = true;
for (var p in parseFloat) {
if (p === "length") {
result = false;
}
}
if (result !== true) {
throw new Test262Error('#2: result = true; for (p in parseFloat) { if (p === "length") result = false; } result === true;');
}

View File

@ -1,25 +0,0 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: The length property of parseFloat does not have the attribute DontDelete
esid: sec-parsefloat-string
description: Checking use hasOwnProperty, delete
---*/
//CHECK#1
if (parseFloat.hasOwnProperty('length') !== true) {
throw new Test262Error('#1: parseFloat.hasOwnProperty(\'length\') === true. Actual: ' + (parseFloat.hasOwnProperty('length')));
}
delete parseFloat.length;
//CHECK#2
if (parseFloat.hasOwnProperty('length') !== false) {
throw new Test262Error('#2: delete parseFloat.length; parseFloat.hasOwnProperty(\'length\') === false. Actual: ' + (parseFloat.hasOwnProperty('length')));
}
//CHECK#3
if (parseFloat.length === undefined) {
throw new Test262Error('#3: delete parseFloat.length; parseFloat.length !== undefined');
}

View File

@ -1,16 +0,0 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: The length property of parseFloat has the attribute ReadOnly
esid: sec-parsefloat-string
description: Checking if varying the length property fails
includes: [propertyHelper.js]
---*/
//CHECK#1
var x = parseFloat.length;
verifyNotWritable(parseFloat, "length", null, Infinity);
if (parseFloat.length !== x) {
throw new Test262Error('#1: x = parseFloat.length; parseFloat.length = Infinity; parseFloat.length === x. Actual: ' + (parseFloat.length));
}

View File

@ -1,13 +0,0 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: The length property of parseFloat is 1
esid: sec-parsefloat-string
description: parseFloat.length === 1
---*/
//CHECK#1
if (parseFloat.length !== 1) {
throw new Test262Error('#1: parseFloat.length === 1. Actual: ' + (parseFloat.length));
}

View File

@ -1,27 +0,0 @@
// Copyright (C) 2015 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-parsefloat-string
description: >
parseFloat.name is "parseFloat".
info: |
parseFloat (string)
17 ECMAScript Standard Built-in Objects:
Every built-in Function object, including constructors, that is not
identified as an anonymous function has a name property whose value
is a String.
Unless otherwise specified, the name property of a built-in Function
object, if it exists, has the attributes { [[Writable]]: false,
[[Enumerable]]: false, [[Configurable]]: true }.
includes: [propertyHelper.js]
---*/
verifyProperty(parseFloat, "name", {
value: "parseFloat",
writable: false,
enumerable: false,
configurable: true
});

View File

@ -12,7 +12,7 @@ info: |
includes: [propertyHelper.js]
---*/
verifyProperty(this, "parseFloat", {
verifyPrimordialCallableProperty(this, "parseFloat", "parseFloat", 1, {
writable: true,
enumerable: false,
configurable: true

View File

@ -1,24 +0,0 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: The length property of parseInt has the attribute DontEnum
esid: sec-parseint-string-radix
description: Checking use propertyIsEnumerable, for-in
---*/
assert.sameValue(
parseInt.propertyIsEnumerable('length'),
false,
'parseInt.propertyIsEnumerable(\'length\') must return false'
);
//CHECK#2
var result = true;
for (var p in parseInt) {
if (p === "length") {
result = false;
}
}
assert.sameValue(result, true, 'The value of `result` is true');

View File

@ -1,15 +0,0 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: The length property of parseInt does not have the attribute DontDelete
esid: sec-parseint-string-radix
description: Checking use hasOwnProperty, delete
---*/
assert.sameValue(parseInt.hasOwnProperty('length'), true, 'parseInt.hasOwnProperty(\'length\') must return true');
delete parseInt.length;
assert.sameValue(parseInt.hasOwnProperty('length'), false, 'parseInt.hasOwnProperty(\'length\') must return false');
assert.notSameValue(parseInt.length, undefined, 'The value of parseInt.length is expected to not equal ``undefined``');

View File

@ -1,12 +0,0 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: The length property of parseInt has the attribute ReadOnly
esid: sec-parseint-string-radix
description: Checking if varying the length property fails
includes: [propertyHelper.js]
---*/
assert.sameValue(parseInt.length, 2, 'The value of parseInt.length is 2');
verifyNotWritable(parseInt, "length", null, Infinity);

View File

@ -1,10 +0,0 @@
// Copyright 2009 the Sputnik authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
info: The length property of parseInt is 2
esid: sec-parseint-string-radix
description: parseInt.length === 2
---*/
assert.sameValue(parseInt.length, 2, 'The value of parseInt.length is 2');

View File

@ -1,27 +0,0 @@
// Copyright (C) 2015 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-parseint-string-radix
description: >
parseInt.name is "parseInt".
info: |
parseInt (string , radix)
17 ECMAScript Standard Built-in Objects:
Every built-in Function object, including constructors, that is not
identified as an anonymous function has a name property whose value
is a String.
Unless otherwise specified, the name property of a built-in Function
object, if it exists, has the attributes { [[Writable]]: false,
[[Enumerable]]: false, [[Configurable]]: true }.
includes: [propertyHelper.js]
---*/
verifyProperty(parseInt, "name", {
value: "parseInt",
writable: false,
enumerable: false,
configurable: true
});

View File

@ -11,7 +11,7 @@ info: |
includes: [propertyHelper.js]
---*/
verifyProperty(this, "parseInt", {
verifyPrimordialCallableProperty(this, "parseInt", "parseInt", 2, {
writable: true,
enumerable: false,
configurable: true