Add various private field and private method tests

This adds tests for implementation bugs in SpiderMonkey [1], plus
additional tests for implementation bugs in V8 and JSC.

[1] https://bugzilla.mozilla.org/show_bug.cgi?id=1723155
This commit is contained in:
André Bargull 2021-08-03 09:17:02 -07:00 committed by Rick Waldron
parent b74b15c49c
commit d00039593d
19 changed files with 724 additions and 0 deletions

View File

@ -0,0 +1,27 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Can't nest `in` expressions when the left-hand side is PrivateIdentifier.
info: |
Syntax
RelationalExpression[In, Yield, Await]:
[...]
[+In]PrivateIdentifier in ShiftExpression[?Yield, ?Await]
esid: sec-relational-operators
negative:
phase: parse
type: SyntaxError
features: [class-fields-private, class-fields-private-in]
---*/
$DONOTEVALUATE();
class C {
#field;
constructor() {
#field in #field in this;
}
}

View File

@ -0,0 +1,25 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Private identifiers aren't valid simple assignment references.
info: |
Syntax
for ( LeftHandSideExpression in Expression ) Statement
esid: sec-for-in-and-for-of-statements-static-semantics-early-errors
negative:
phase: parse
type: SyntaxError
features: [class-fields-private, class-fields-private-in]
---*/
$DONOTEVALUATE();
class C {
#field;
m() {
for (#field in []) ;
}
}

View File

@ -0,0 +1,38 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Trying to get a private member without getter throws TypeError
esid: sec-privatefieldget
info: |
PrivateFieldGet ( P, O )
1. Assert: P is a Private Name.
2. If O is not an object, throw a TypeError exception.
3. If P.[[Kind]] is "field",
a. Let entry be PrivateFieldFind(P, O).
b. If entry is empty, throw a TypeError exception.
c. Return entry.[[PrivateFieldValue]].
4. Perform ? PrivateBrandCheck(O, P).
5. If P.[[Kind]] is "method",
a. Return P.[[Value]].
6. Else,
a. Assert: P.[[Kind]] is "accessor".
b. If P does not have a [[Get]] field, throw a TypeError exception.
c. Let getter be P.[[Get]].
d. Return ? Call(getter, O).
features: [class-static-methods-private, class]
---*/
class C {
static set #f(v) {
throw new Test262Error();
}
static getAccess() {
return this.#f;
}
}
assert.throws(TypeError, function() {
C.getAccess();
}, 'get operation on private accessor without getter should throw TypeError');

View File

@ -0,0 +1,35 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Throws TypeError when attempting to install private methods multiple times.
esid: sec-privatemethodoraccessoradd
info: |
7.3.28 PrivateMethodOrAccessorAdd ( method, O )
1. Assert: method.[[Kind]] is either method or accessor.
2. Let entry be ! PrivateElementFind(method.[[Key]], O).
3. If entry is not empty, throw a TypeError exception.
...
features: [class, class-methods-private]
---*/
class Base {
constructor(o) {
return o;
}
}
class C extends Base {
get #p() {}
set #p(x) {}
}
var obj = {};
new C(obj);
assert.throws(TypeError, function() {
new C(obj);
});

View File

@ -0,0 +1,34 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Throws TypeError when attempting to install private methods multiple times.
esid: sec-privatemethodoraccessoradd
info: |
7.3.28 PrivateMethodOrAccessorAdd ( method, O )
1. Assert: method.[[Kind]] is either method or accessor.
2. Let entry be ! PrivateElementFind(method.[[Key]], O).
3. If entry is not empty, throw a TypeError exception.
...
features: [class, class-methods-private]
---*/
class Base {
constructor(o) {
return o;
}
}
class C extends Base {
get #p() {}
}
var obj = {};
new C(obj);
assert.throws(TypeError, function() {
new C(obj);
});

View File

@ -0,0 +1,34 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Throws TypeError when attempting to install private methods multiple times.
esid: sec-privatemethodoraccessoradd
info: |
7.3.28 PrivateMethodOrAccessorAdd ( method, O )
1. Assert: method.[[Kind]] is either method or accessor.
2. Let entry be ! PrivateElementFind(method.[[Key]], O).
3. If entry is not empty, throw a TypeError exception.
...
features: [class, class-methods-private]
---*/
class Base {
constructor(o) {
return o;
}
}
class C extends Base {
set #p(x) {}
}
var obj = {};
new C(obj);
assert.throws(TypeError, function() {
new C(obj);
});

View File

@ -0,0 +1,34 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Throws TypeError when attempting to install private methods multiple times.
esid: sec-privatemethodoraccessoradd
info: |
7.3.28 PrivateMethodOrAccessorAdd ( method, O )
1. Assert: method.[[Kind]] is either method or accessor.
2. Let entry be ! PrivateElementFind(method.[[Key]], O).
3. If entry is not empty, throw a TypeError exception.
...
features: [class, class-methods-private]
---*/
class Base {
constructor(o) {
return o;
}
}
class C extends Base {
#m() {}
}
var obj = {};
new C(obj);
assert.throws(TypeError, function() {
new C(obj);
});

View File

@ -0,0 +1,33 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Throws TypeError when attempting to overwrite a private method.
esid: sec-privateset
info: |
7.3.30 PrivateSet ( P, O, value )
1. Let entry be ! PrivateElementFind(P, O).
2. If entry is empty, throw a TypeError exception.
3. If entry.[[Kind]] is field, then
...
4. Else if entry.[[Kind]] is method, then
a. Throw a TypeError exception.
5. ...
features: [class, class-methods-private]
---*/
class C {
#m() {}
assign() {
this.#m = 0;
}
}
var obj = new C();
assert.throws(TypeError, function() {
obj.assign();
});

View File

@ -0,0 +1,31 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Throws TypeError when attempting to overwrite a private static method.
esid: sec-privateset
info: |
7.3.30 PrivateSet ( P, O, value )
1. Let entry be ! PrivateElementFind(P, O).
2. If entry is empty, throw a TypeError exception.
3. If entry.[[Kind]] is field, then
...
4. Else if entry.[[Kind]] is method, then
a. Throw a TypeError exception.
5. ...
features: [class, class-static-methods-private]
---*/
class C {
static #m() {}
static assign() {
this.#m = 0;
}
}
assert.throws(TypeError, function() {
C.assign();
});

View File

@ -0,0 +1,43 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Evaluation order when resolving private fields.
esid: sec-runtime-semantics-keyeddestructuringassignmentevaluation
info: |
13.15.5.6 Runtime Semantics: KeyedDestructuringAssignmentEvaluation
1. If DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral, then
a. Let lref be the result of evaluating DestructuringAssignmentTarget.
b. ReturnIfAbrupt(lref).
2. ...
9.1.1.3.4 GetThisBinding ( )
1. Assert: envRec.[[ThisBindingStatus]] is not lexical.
2. If envRec.[[ThisBindingStatus]] is uninitialized, throw a ReferenceError exception.
3. ...
features: [class, class-fields-private]
---*/
class C extends class {} {
#field;
constructor() {
var init = () => super();
var object = {
get a() {
init();
}
};
// Accessing |this| should throw a ReferenceError before there's an attempt
// to invoke the getter.
({a: this.#field} = object);
}
}
assert.throws(ReferenceError, function() {
new C();
});

View File

@ -0,0 +1,36 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Evaluation order when resolving private fields.
esid: sec-runtime-semantics-keyeddestructuringassignmentevaluation
info: |
13.15.5.6 Runtime Semantics: KeyedDestructuringAssignmentEvaluation
1. If DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral, then
a. Let lref be the result of evaluating DestructuringAssignmentTarget.
b. ReturnIfAbrupt(lref).
2. Let v be ? GetV(value, propertyName).
3. ...
features: [class, class-fields-private]
---*/
class C {
#field;
m() {
var object = {
get a() {
throw new Test262Error();
}
};
// The getter is executed before the check if the private field is present.
({a: this.#field} = object);
}
}
assert.throws(Test262Error, function() {
C.prototype.m.call({});
});

View File

@ -0,0 +1,45 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Evaluation order when resolving private fields.
esid: sec-runtime-semantics-keyeddestructuringassignmentevaluation
info: |
13.15.5.6 Runtime Semantics: KeyedDestructuringAssignmentEvaluation
1. If DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral, then
a. Let lref be the result of evaluating DestructuringAssignmentTarget.
b. ReturnIfAbrupt(lref).
2. Let v be ? GetV(value, propertyName).
3. ...
features: [class, class-fields-private]
---*/
class Base {
constructor(o) {
return o;
}
}
class C extends Base {
#field;
m() {
var init = () => new C(this);
var object = {
get a() {
init();
return "pass";
}
};
({a: this.#field} = object);
assert.sameValue(this.#field, "pass");
}
}
C.prototype.m.call({});

View File

@ -0,0 +1,45 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
TypeError when setting private field not in `this`'s [[PrivateFieldValues]]
esid: sec-putvalue
info: |
PutValue ( V, W )
...
6. Else if IsPropertyReference(V), then
...
b. If IsPrivateReference(V), then
i. Let env be the running execution context's PrivateNameEnvironment.
ii. Let field be ? ResolveBinding(GetReferencedName(V), env).
iii. Assert: field is a Private Name.
iv. Perform ? PrivateFieldSet(field, base, W).
PrivateFieldSet (P, O, value )
1. Assert: P is a Private Name value.
2. If O is not an object, throw a TypeError exception.
3. Let entry be PrivateFieldFind(P, O).
4. If entry is empty, throw a TypeError exception.
PrivateFieldFind (P, O)
1. Assert: P is a Private Name value.
2. Assert: O is an object with a [[PrivateFieldValues]] internal slot.
3. For each element entry in O.[[PrivateFieldValues]],
a. If entry.[[PrivateName]] is P, return entry.
4. Return empty.
features: [class, class-fields-private]
---*/
class C {
#field;
m() {
[...this.#field] = [];
}
}
assert.throws(TypeError, function() {
C.prototype.m.call({});
});

View File

@ -0,0 +1,45 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
TypeError when setting private field not in `this`'s [[PrivateFieldValues]]
esid: sec-putvalue
info: |
PutValue ( V, W )
...
6. Else if IsPropertyReference(V), then
...
b. If IsPrivateReference(V), then
i. Let env be the running execution context's PrivateNameEnvironment.
ii. Let field be ? ResolveBinding(GetReferencedName(V), env).
iii. Assert: field is a Private Name.
iv. Perform ? PrivateFieldSet(field, base, W).
PrivateFieldSet (P, O, value )
1. Assert: P is a Private Name value.
2. If O is not an object, throw a TypeError exception.
3. Let entry be PrivateFieldFind(P, O).
4. If entry is empty, throw a TypeError exception.
PrivateFieldFind (P, O)
1. Assert: P is a Private Name value.
2. Assert: O is an object with a [[PrivateFieldValues]] internal slot.
3. For each element entry in O.[[PrivateFieldValues]],
a. If entry.[[PrivateName]] is P, return entry.
4. Return empty.
features: [class, class-fields-private]
---*/
class C {
#field;
m() {
({...this.#field} = {});
}
}
assert.throws(TypeError, function() {
C.prototype.m.call({});
});

View File

@ -0,0 +1,45 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
TypeError when setting private field not in `this`'s [[PrivateFieldValues]]
esid: sec-putvalue
info: |
PutValue ( V, W )
...
6. Else if IsPropertyReference(V), then
...
b. If IsPrivateReference(V), then
i. Let env be the running execution context's PrivateNameEnvironment.
ii. Let field be ? ResolveBinding(GetReferencedName(V), env).
iii. Assert: field is a Private Name.
iv. Perform ? PrivateFieldSet(field, base, W).
PrivateFieldSet (P, O, value )
1. Assert: P is a Private Name value.
2. If O is not an object, throw a TypeError exception.
3. Let entry be PrivateFieldFind(P, O).
4. If entry is empty, throw a TypeError exception.
PrivateFieldFind (P, O)
1. Assert: P is a Private Name value.
2. Assert: O is an object with a [[PrivateFieldValues]] internal slot.
3. For each element entry in O.[[PrivateFieldValues]],
a. If entry.[[PrivateName]] is P, return entry.
4. Return empty.
features: [class, class-fields-private]
---*/
class C {
#field;
m() {
for (this.#field of [1]) ;
}
}
assert.throws(TypeError, function() {
C.prototype.m.call({});
});

View File

@ -0,0 +1,45 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
TypeError when setting private field not in `this`'s [[PrivateFieldValues]]
esid: sec-putvalue
info: |
PutValue ( V, W )
...
6. Else if IsPropertyReference(V), then
...
b. If IsPrivateReference(V), then
i. Let env be the running execution context's PrivateNameEnvironment.
ii. Let field be ? ResolveBinding(GetReferencedName(V), env).
iii. Assert: field is a Private Name.
iv. Perform ? PrivateFieldSet(field, base, W).
PrivateFieldSet (P, O, value )
1. Assert: P is a Private Name value.
2. If O is not an object, throw a TypeError exception.
3. Let entry be PrivateFieldFind(P, O).
4. If entry is empty, throw a TypeError exception.
PrivateFieldFind (P, O)
1. Assert: P is a Private Name value.
2. Assert: O is an object with a [[PrivateFieldValues]] internal slot.
3. For each element entry in O.[[PrivateFieldValues]],
a. If entry.[[PrivateName]] is P, return entry.
4. Return empty.
features: [class, class-fields-private]
---*/
class C {
#field;
m() {
for (this.#field in {a: 0}) ;
}
}
assert.throws(TypeError, function() {
C.prototype.m.call({});
});

View File

@ -0,0 +1,45 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
TypeError when setting private field not in `this`'s [[PrivateFieldValues]]
esid: sec-putvalue
info: |
PutValue ( V, W )
...
6. Else if IsPropertyReference(V), then
...
b. If IsPrivateReference(V), then
i. Let env be the running execution context's PrivateNameEnvironment.
ii. Let field be ? ResolveBinding(GetReferencedName(V), env).
iii. Assert: field is a Private Name.
iv. Perform ? PrivateFieldSet(field, base, W).
PrivateFieldSet (P, O, value )
1. Assert: P is a Private Name value.
2. If O is not an object, throw a TypeError exception.
3. Let entry be PrivateFieldFind(P, O).
4. If entry is empty, throw a TypeError exception.
PrivateFieldFind (P, O)
1. Assert: P is a Private Name value.
2. Assert: O is an object with a [[PrivateFieldValues]] internal slot.
3. For each element entry in O.[[PrivateFieldValues]],
a. If entry.[[PrivateName]] is P, return entry.
4. Return empty.
features: [class, class-fields-private]
---*/
class C {
#field;
m() {
[this.#field] = [1];
}
}
assert.throws(TypeError, function() {
C.prototype.m.call({});
});

View File

@ -0,0 +1,45 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
TypeError when setting private field not in `this`'s [[PrivateFieldValues]]
esid: sec-putvalue
info: |
PutValue ( V, W )
...
6. Else if IsPropertyReference(V), then
...
b. If IsPrivateReference(V), then
i. Let env be the running execution context's PrivateNameEnvironment.
ii. Let field be ? ResolveBinding(GetReferencedName(V), env).
iii. Assert: field is a Private Name.
iv. Perform ? PrivateFieldSet(field, base, W).
PrivateFieldSet (P, O, value )
1. Assert: P is a Private Name value.
2. If O is not an object, throw a TypeError exception.
3. Let entry be PrivateFieldFind(P, O).
4. If entry is empty, throw a TypeError exception.
PrivateFieldFind (P, O)
1. Assert: P is a Private Name value.
2. Assert: O is an object with a [[PrivateFieldValues]] internal slot.
3. For each element entry in O.[[PrivateFieldValues]],
a. If entry.[[PrivateName]] is P, return entry.
4. Return empty.
features: [class, class-fields-private]
---*/
class C {
#field;
m() {
({a: this.#field} = {a: 0});
}
}
assert.throws(TypeError, function() {
C.prototype.m.call({});
});

View File

@ -0,0 +1,39 @@
// Copyright (C) 2021 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Trying to set a private member without setter throws TypeError
esid: sec-privatefieldset
info: |
PrivateFieldSet ( P, O, value )
1. Assert: P is a Private Name.
2. If O is not an object, throw a TypeError exception.
3. If P.[[Kind]] is "field",
a. Let entry be PrivateFieldFind(P, O).
b. If entry is empty, throw a TypeError exception.
c. Set entry.[[PrivateFieldValue]] to value.
d. Return.
4. If P.[[Kind]] is "method", throw a TypeError exception.
5. Else,
a. Assert: P.[[Kind]] is "accessor".
b. If O.[[PrivateFieldBrands]] does not contain P.[[Brand]], throw a TypeError exception.
c. If P does not have a [[Set]] field, throw a TypeError exception.
d. Let setter be P.[[Set]].
e. Perform ? Call(setter, O, value).
f. Return.
features: [class-static-methods-private, class]
---*/
class C {
static get #f() {
throw new Test262Error();
}
static setAccess() {
this.#f = 'Test262';
}
}
assert.throws(TypeError, function() {
C.setAccess();
}, 'set operation on private accessor without setter should throw TypeError');