From 2020cfbe66f213209fa1fedff62d6e656139f757 Mon Sep 17 00:00:00 2001 From: Luis Fernando Pardo Sixtos Date: Tue, 13 Aug 2024 10:19:32 -0700 Subject: [PATCH] [decorators] Support private auto-accessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for the accessor keywords for private class fields as part of the decorators proposal. Changes to AST: - Add an AUTO_ACCESSOR value to the ClassLiteralProperty::Kind enum. - Add an AutoAccessorInfo class to be used in ClassLiteralProperty objects of kind AUTO_ACCESSOR to hold the information about the generated getters/setters and backing storage. - Add AutoAccessorGetterBody and AutoAccessorSetterBody statements to implement the logic of generated getters and setters. Changes to Parser: - Add logic to parse the "accessor" keyword and throw when used on non field class properties. - Add preparser logic to mock the function scopes and variable declarations required for the generated getters/setters. - Add parser logic to synthetically create statements for the generated setters/getters.  Changes to the Bytecode Generator: - Add logic to BuildClassLiteral to build auto accessor storage private names. - Add logic to set the generated getters/setters in the accessor pair. - Add logic to initialize the accessor storage in BuildClassProperty. - Add AutoAccessorGetterBody and AutoAccessorSetterBody visitors. Tests: - Add parsing-unittests for parsing converage. - Add test262 tests for functionality coverage. - Add test-debug test for devtools support coverage. Bug: 42202709 Change-Id: Ibb9bee3bbd0c09341108856f969e0c22bbb8b9cc Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5547688 Reviewed-by: Seth Brenith Commit-Queue: Luis Pardo Reviewed-by: Shu-yu Guo Cr-Commit-Position: refs/heads/main@{#95612} --- .../decorators/private-auto-accessor.js | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 test/staging/decorators/private-auto-accessor.js diff --git a/test/staging/decorators/private-auto-accessor.js b/test/staging/decorators/private-auto-accessor.js new file mode 100644 index 0000000000..7edbd2566c --- /dev/null +++ b/test/staging/decorators/private-auto-accessor.js @@ -0,0 +1,106 @@ +// Copyright 2024 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/*--- +description: Test private auto-accessors. +features: [decorators] +---*/ + +(function TestUninitializedPrivateAccessor() { + class C { + accessor #x; + assertFieldIsUndefined() { + assert.sameValue(this.#x, undefined); + } + assertFieldMatchesValue(value) { + assert.sameValue(this.#x, value); + } + setField(value) { + this.#x = value; + } + } + let c = new C(); + c.assertFieldIsUndefined(); + c.setField(42); + c.assertFieldMatchesValue(42); +})(); + +(function TestInitializedPrivateAccessor() { + class C { + accessor #x = 5; + assertFieldMatchesValue(value) { + assert.sameValue(this.#x, value); + } + setField(value) { + this.#x = value; + } + } + let c = new C(); + c.assertFieldMatchesValue(5); + c.setField(42); + c.assertFieldMatchesValue(42); +})(); + +(function TestInitializedMultiplePrivateAccessor() { + class C { + accessor #x = 5; + accessor #y = 42; + assertFieldsMatcheValues(value_x, value_y) { + assert.sameValue(this.#x, value_x); + assert.sameValue(this.#y, value_y); + } + } + let c = new C(); + c.assertFieldsMatcheValues(5, 42); +})(); + +(function TestThrowOnDuplicatedName() { + // Auto-accessors will instantiate private getter and setter with the same + // name, which shouldn't collide with other private variables. + let assertThrowsSyntaxError = (code_string) => { + assert.throws(SyntaxError, () => {eval(code_string)}); + }; + assertThrowsSyntaxError('class C { accessor #x = 5; accessor #x = 42; }'); + assertThrowsSyntaxError('class C { accessor #x = 5; #x = 42; }'); + assertThrowsSyntaxError('class C { accessor #x = 5; get #x() {}; }'); + assertThrowsSyntaxError('class C { accessor #x = 5; set #x(value) {}; }'); + assertThrowsSyntaxError('class C { accessor #x = 5; #x() {}; }'); + assertThrowsSyntaxError('class C { accessor #x = 5; static #x = 42; }'); + assertThrowsSyntaxError('class C { static accessor #x = 5; #x = 42; }'); + assertThrowsSyntaxError( + 'class C { static accessor #x = 5; static #x = 42; }'); +})(); + +(function TestUninitializedStaticPrivateAccessor() { + class C { + static #x; + static assertFieldIsUndefined() { + assert.sameValue(C.#x, undefined); + } + static assertFieldMatchesValue(value) { + assert.sameValue(C.#x, value); + } + static setField(value) { + C.#x = value; + } + } + C.assertFieldIsUndefined(); + C.setField(42); + C.assertFieldMatchesValue(42); +})(); + +(function TestInitializedStaticPrivateAccessor() { + class C { + static accessor #x = 5; + static assertFieldMatchesValue(value) { + assert.sameValue(C.#x, value); + } + static setField(value) { + C.#x = value; + } + } + C.assertFieldMatchesValue(5); + C.setField(42); + C.assertFieldMatchesValue(42); +})();