From d00039593dd85ddd809555d061e84dce1eb2a303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Tue, 3 Aug 2021 09:17:02 -0700 Subject: [PATCH] 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 --- .../expressions/in/private-field-in-nested.js | 27 +++++++++++ ...vate-field-invalid-assignment-reference.js | 25 +++++++++++ ...access-of-missing-private-static-getter.js | 38 ++++++++++++++++ ...ethod-double-initialisation-get-and-set.js | 35 +++++++++++++++ ...rivate-method-double-initialisation-get.js | 34 ++++++++++++++ ...rivate-method-double-initialisation-set.js | 34 ++++++++++++++ .../private-method-double-initialisation.js | 34 ++++++++++++++ .../elements/private-method-not-writable.js | 33 ++++++++++++++ .../private-static-method-not-writable.js | 31 +++++++++++++ .../privatefieldset-evaluation-order-1.js | 43 ++++++++++++++++++ .../privatefieldset-evaluation-order-2.js | 36 +++++++++++++++ .../privatefieldset-evaluation-order-3.js | 45 +++++++++++++++++++ .../elements/privatefieldset-typeerror-10.js | 45 +++++++++++++++++++ .../elements/privatefieldset-typeerror-11.js | 45 +++++++++++++++++++ .../elements/privatefieldset-typeerror-6.js | 45 +++++++++++++++++++ .../elements/privatefieldset-typeerror-7.js | 45 +++++++++++++++++++ .../elements/privatefieldset-typeerror-8.js | 45 +++++++++++++++++++ .../elements/privatefieldset-typeerror-9.js | 45 +++++++++++++++++++ ...access-of-missing-private-static-setter.js | 39 ++++++++++++++++ 19 files changed, 724 insertions(+) create mode 100644 test/language/expressions/in/private-field-in-nested.js create mode 100644 test/language/expressions/in/private-field-invalid-assignment-reference.js create mode 100644 test/language/statements/class/elements/get-access-of-missing-private-static-getter.js create mode 100644 test/language/statements/class/elements/private-method-double-initialisation-get-and-set.js create mode 100644 test/language/statements/class/elements/private-method-double-initialisation-get.js create mode 100644 test/language/statements/class/elements/private-method-double-initialisation-set.js create mode 100644 test/language/statements/class/elements/private-method-double-initialisation.js create mode 100644 test/language/statements/class/elements/private-method-not-writable.js create mode 100644 test/language/statements/class/elements/private-static-method-not-writable.js create mode 100644 test/language/statements/class/elements/privatefieldset-evaluation-order-1.js create mode 100644 test/language/statements/class/elements/privatefieldset-evaluation-order-2.js create mode 100644 test/language/statements/class/elements/privatefieldset-evaluation-order-3.js create mode 100644 test/language/statements/class/elements/privatefieldset-typeerror-10.js create mode 100644 test/language/statements/class/elements/privatefieldset-typeerror-11.js create mode 100644 test/language/statements/class/elements/privatefieldset-typeerror-6.js create mode 100644 test/language/statements/class/elements/privatefieldset-typeerror-7.js create mode 100644 test/language/statements/class/elements/privatefieldset-typeerror-8.js create mode 100644 test/language/statements/class/elements/privatefieldset-typeerror-9.js create mode 100644 test/language/statements/class/elements/set-access-of-missing-private-static-setter.js diff --git a/test/language/expressions/in/private-field-in-nested.js b/test/language/expressions/in/private-field-in-nested.js new file mode 100644 index 0000000000..60da762cb7 --- /dev/null +++ b/test/language/expressions/in/private-field-in-nested.js @@ -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; + } +} diff --git a/test/language/expressions/in/private-field-invalid-assignment-reference.js b/test/language/expressions/in/private-field-invalid-assignment-reference.js new file mode 100644 index 0000000000..bc5b5cb38e --- /dev/null +++ b/test/language/expressions/in/private-field-invalid-assignment-reference.js @@ -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 []) ; + } +} diff --git a/test/language/statements/class/elements/get-access-of-missing-private-static-getter.js b/test/language/statements/class/elements/get-access-of-missing-private-static-getter.js new file mode 100644 index 0000000000..0b4d5cd361 --- /dev/null +++ b/test/language/statements/class/elements/get-access-of-missing-private-static-getter.js @@ -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'); diff --git a/test/language/statements/class/elements/private-method-double-initialisation-get-and-set.js b/test/language/statements/class/elements/private-method-double-initialisation-get-and-set.js new file mode 100644 index 0000000000..2050d65fe3 --- /dev/null +++ b/test/language/statements/class/elements/private-method-double-initialisation-get-and-set.js @@ -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); +}); diff --git a/test/language/statements/class/elements/private-method-double-initialisation-get.js b/test/language/statements/class/elements/private-method-double-initialisation-get.js new file mode 100644 index 0000000000..eaecde21e8 --- /dev/null +++ b/test/language/statements/class/elements/private-method-double-initialisation-get.js @@ -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); +}); diff --git a/test/language/statements/class/elements/private-method-double-initialisation-set.js b/test/language/statements/class/elements/private-method-double-initialisation-set.js new file mode 100644 index 0000000000..c06998f132 --- /dev/null +++ b/test/language/statements/class/elements/private-method-double-initialisation-set.js @@ -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); +}); diff --git a/test/language/statements/class/elements/private-method-double-initialisation.js b/test/language/statements/class/elements/private-method-double-initialisation.js new file mode 100644 index 0000000000..700f58c8f0 --- /dev/null +++ b/test/language/statements/class/elements/private-method-double-initialisation.js @@ -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); +}); diff --git a/test/language/statements/class/elements/private-method-not-writable.js b/test/language/statements/class/elements/private-method-not-writable.js new file mode 100644 index 0000000000..aa77efebca --- /dev/null +++ b/test/language/statements/class/elements/private-method-not-writable.js @@ -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(); +}); diff --git a/test/language/statements/class/elements/private-static-method-not-writable.js b/test/language/statements/class/elements/private-static-method-not-writable.js new file mode 100644 index 0000000000..ab54e915ac --- /dev/null +++ b/test/language/statements/class/elements/private-static-method-not-writable.js @@ -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(); +}); diff --git a/test/language/statements/class/elements/privatefieldset-evaluation-order-1.js b/test/language/statements/class/elements/privatefieldset-evaluation-order-1.js new file mode 100644 index 0000000000..e2b375813d --- /dev/null +++ b/test/language/statements/class/elements/privatefieldset-evaluation-order-1.js @@ -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(); +}); diff --git a/test/language/statements/class/elements/privatefieldset-evaluation-order-2.js b/test/language/statements/class/elements/privatefieldset-evaluation-order-2.js new file mode 100644 index 0000000000..e13a79da1a --- /dev/null +++ b/test/language/statements/class/elements/privatefieldset-evaluation-order-2.js @@ -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({}); +}); diff --git a/test/language/statements/class/elements/privatefieldset-evaluation-order-3.js b/test/language/statements/class/elements/privatefieldset-evaluation-order-3.js new file mode 100644 index 0000000000..f653bb533e --- /dev/null +++ b/test/language/statements/class/elements/privatefieldset-evaluation-order-3.js @@ -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({}); diff --git a/test/language/statements/class/elements/privatefieldset-typeerror-10.js b/test/language/statements/class/elements/privatefieldset-typeerror-10.js new file mode 100644 index 0000000000..f55f804c3d --- /dev/null +++ b/test/language/statements/class/elements/privatefieldset-typeerror-10.js @@ -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({}); +}); diff --git a/test/language/statements/class/elements/privatefieldset-typeerror-11.js b/test/language/statements/class/elements/privatefieldset-typeerror-11.js new file mode 100644 index 0000000000..24658b6684 --- /dev/null +++ b/test/language/statements/class/elements/privatefieldset-typeerror-11.js @@ -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({}); +}); diff --git a/test/language/statements/class/elements/privatefieldset-typeerror-6.js b/test/language/statements/class/elements/privatefieldset-typeerror-6.js new file mode 100644 index 0000000000..fb916157e5 --- /dev/null +++ b/test/language/statements/class/elements/privatefieldset-typeerror-6.js @@ -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({}); +}); diff --git a/test/language/statements/class/elements/privatefieldset-typeerror-7.js b/test/language/statements/class/elements/privatefieldset-typeerror-7.js new file mode 100644 index 0000000000..8679407bba --- /dev/null +++ b/test/language/statements/class/elements/privatefieldset-typeerror-7.js @@ -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({}); +}); diff --git a/test/language/statements/class/elements/privatefieldset-typeerror-8.js b/test/language/statements/class/elements/privatefieldset-typeerror-8.js new file mode 100644 index 0000000000..0298cd3d72 --- /dev/null +++ b/test/language/statements/class/elements/privatefieldset-typeerror-8.js @@ -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({}); +}); diff --git a/test/language/statements/class/elements/privatefieldset-typeerror-9.js b/test/language/statements/class/elements/privatefieldset-typeerror-9.js new file mode 100644 index 0000000000..6b64462bd4 --- /dev/null +++ b/test/language/statements/class/elements/privatefieldset-typeerror-9.js @@ -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({}); +}); diff --git a/test/language/statements/class/elements/set-access-of-missing-private-static-setter.js b/test/language/statements/class/elements/set-access-of-missing-private-static-setter.js new file mode 100644 index 0000000000..e821b5fe1f --- /dev/null +++ b/test/language/statements/class/elements/set-access-of-missing-private-static-setter.js @@ -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');